
1. IMR——桌面 GPU 的渲染流程
1.1 IMR 渲染特點(diǎn)
DrawCall 中的模型順序執(zhí)行 VS 和 PS
每個(gè)DrawCall 完成后,PS 將所有像素顏色、深度等寫入 FrameBuffer
每個(gè)像素可以被多次寫入,寫入順序和 DrawCall 執(zhí)行順序一致
1.2 作為寫入目標(biāo)的FrameBuffer ,對(duì)顯存的性能要求
訪問速度:極高的訪問速度
容量要求:滿足響應(yīng)分辨率和格式(RGBA,HDR等)的容量
功耗:電池供電設(shè)備的低功耗要求
在目前的移動(dòng)設(shè)備硬件水平下,這三個(gè)目標(biāo)通常最多只能滿足兩個(gè)。目前的主流選擇:犧牲容量,換取更快的訪問速度和更低的功耗。
2. 解決方案:TBR(Tile Based Rendering)

2.1 SIMD 核心不直接寫到 FrameBuffer,而是寫到TileBuffer,TileBuffer 的內(nèi)容在恰當(dāng)?shù)臅r(shí)候?qū)懙?FrameBuffer
小容量、高速 On chip 的 TileBuffer 作為 SIMD 的寫入目標(biāo)
FrameBuffer 會(huì)分塊依次渲染
單Tile上所有任務(wù),即每個(gè) Tile 上的 DrawCall 都完成后才一次寫入顯存

2.2 TBR 中的深度測(cè)試

- Early-Z:在 PS 之前執(zhí)行,測(cè)試失敗的像素不執(zhí)行PS
- Late-Z:在 PS 之后執(zhí)行,測(cè)試失敗的像素不寫入 Color Buffer
- 可見 Early-Z 能顯著降低 PS 的性能壓力,最好在無法應(yīng)用 Early-Z 的時(shí)候再啟用 Late-Z
什么情況下 Early-Z 失效?
因?yàn)?Early-Z 在PS 之前執(zhí)行,所以需要必須在 PS 之前就確定深度,也就是說 執(zhí)行PS時(shí)像素深度不能發(fā)生變化
- Alpha Test / Clip:需要執(zhí)行完 PS 后,才能確定該像素深度是否被寫入
- Custom depth,PS中改寫深度值,硬件能夠感知,Early-Z 階段無法獲取最終的深度值,失效。
2.3 移動(dòng)端 GPU 的 Early-Z

- TileList 保存當(dāng)前 Tile 上的所有三角形列表
- 隱藏面剔除HSR(Apple/PowerVR), LRZ(Adreno), FPK(Mali)
- 原理:硬件層面先對(duì) Tile 中的三角面進(jìn)行 Depth Prepass,相當(dāng)于先進(jìn)行了一邊深度測(cè)試,剔除掉看不到的 fragment,可以顯著降低 overdraw 程度,減少 PS 調(diào)用次數(shù)
- 移動(dòng)端 Early-Z 失效:打斷了硬件的 Depth Prepass
2.4 性能關(guān)注點(diǎn)
2.4.1 頂點(diǎn)開銷
- TBR 中頂點(diǎn)會(huì)存在 TileList 中,過多的頂點(diǎn)會(huì)使得 TileList 過大,影響訪問性能和內(nèi)存開銷
- 移動(dòng)端 Early-Z 雖然會(huì)降低 PS 開銷,但是會(huì)增加 VS 開銷(額外做一遍 Depth Prepass,有些廠商的GPU實(shí)際上是執(zhí)行了良次 VS,其中一次會(huì)優(yōu)化掉和位置計(jì)算無關(guān)的部分),因此優(yōu)化 VS 復(fù)雜度(特別是位置計(jì)算)尤為重要
2.4.2 Tessellation
- 移動(dòng) GPU 中通常沒有專門的 Tessellation 單元,效率更低
- Tessellation 產(chǎn)生更多頂點(diǎn),需要執(zhí)行更多次 VS,增加開銷
2.4.3 充分利用 TileBuffer
TileBuffer 和主顯存之間消耗大量帶寬,針對(duì)這個(gè)消耗進(jìn)行如下的優(yōu)化
- 每一幀對(duì) TileBuffer 執(zhí)行Clear ,避免上一幀的 Color Buffer/ Depth Buffer 又被從主存加載一邊
- 渲染完成后 Discard 掉不需要的 Render Target 或 Depth Buffer,避免會(huì)寫到顯存,節(jié)省大量帶寬。
3. TBDR:基于 TileBuffer 的延遲渲染
3.1 延遲渲染傳統(tǒng)流程

- 需要提供較多的顯存空間,支持多張 RT(MRT),也就是GBuffer,寫入材質(zhì)各種屬性
- 獨(dú)立的一個(gè) LightPass,讀入每個(gè)像素的GBuffer屬性,并進(jìn)行光照處理,得到最終的 ColorBuffer
- 移動(dòng)平臺(tái)上難以實(shí)現(xiàn)的原因:延遲渲染因?yàn)?MRT 的存在,大量消耗顯存讀寫的帶寬
3.2 移動(dòng)設(shè)備上的延遲渲染(TBDR)

- 像素的屬性(MRT)不被寫入主顯存,只存在于 TileBuffer中,最終只復(fù)制 Color 信息到主顯存中,降低了帶寬消耗
- 幾乎不會(huì)占用額外帶寬
4. 移動(dòng)設(shè)備上的 MSAA
4.1 傳統(tǒng) MSAA 流程

- 需要一個(gè)4倍RT來獲得4倍的fragment,最后再 resolve 會(huì)1倍的 RT
4.2 移動(dòng)設(shè)備 MSAA 流程

- 多倍采樣的 RT 僅在 TileBuffer 中存在
- Resolve 發(fā)生在 TileBuffer 中,逐 Tile 執(zhí)行
- 僅單倍采樣的 RT 被寫回顯存
5. GPU 和 CPU 協(xié)作(Frame Pacing)
5.1 基本的 FramePacing 流程

- CPU 和 GPU 異步運(yùn)行
- Command Buffer 作為 CPU 和 GPU 的協(xié)議包
- CPU 填充 CommandBuffer,在提交后(DrawCall)后 GPU 才開始執(zhí)行 CommandBuffer
5.2 對(duì) FramePacing 進(jìn)行優(yōu)化
目標(biāo):最大程度的并行化
- 盡早提交渲染命令,讓 GPU 盡早開始工作,但避免將 CommandBuffer 拆分過于細(xì)碎,提交本身也有消耗
- 非渲染相關(guān)的 CPU 任務(wù)不必要等待上一幀渲染完成(不要等 GameUpdate)
- 保存數(shù)據(jù)單向流動(dòng)(CPU到GPU),避免單幀內(nèi) CPU 依賴 GPU 執(zhí)行結(jié)果
CPU 幀內(nèi)依賴于 GPU 執(zhí)行結(jié)果的一個(gè)例子是 “硬件遮擋查詢”機(jī)制,它需要 CPU 創(chuàng)建遮擋查詢命令提交給 GPU,等待 GPU 的運(yùn)算結(jié)果,再根據(jù)結(jié)果執(zhí)行渲染。
5.3 垂直同步
渲染結(jié)果只能在固定的時(shí)間點(diǎn)顯示到屏幕,比如移動(dòng)設(shè)備的屏幕刷新率為 60Hz,意思是手機(jī)屏幕每秒最多進(jìn)行 60 次的緩沖區(qū)交換(渲染刷新)
問題:
- 若某幀渲染太快,到了第一個(gè)同步點(diǎn)就刷新到屏幕了,而下一陣太慢,第二個(gè)同步點(diǎn)才刷新,則幀率不穩(wěn)定,有卡頓感
- 若刷新率慢而幀渲染率快,未開啟垂直同步的情況下,可能畫面撕裂,上一個(gè) FrameBuffer 才顯示一半,下一幀 FrameBuffer 已經(jīng)提交,再刷新就變成了下一幀的畫面
- 移動(dòng)設(shè)備上始終開啟垂直同步,保證最小的幀間隔時(shí)間
- 做幀率限制時(shí),保持最大幀率和屏幕刷新率整除的關(guān)系,如60Hz設(shè)備可限制 30FPS,20FPS,但不要限制 25FPS
- 使用圖形層 API 接口來限制