PattiSmith
1. IMR——桌面 GPU 的渲染流程
1.1 IMR 渲染特點
DrawCall 中的模型順序執(zhí)行 VS 和 PS
每個DrawCall 完成后檩淋,PS 將所有像素顏色、深度等寫入 FrameBuffer
每個像素可以被多次寫入模闲,寫入順序和 DrawCall 執(zhí)行順序一致
1.2 作為寫入目標(biāo)的FrameBuffer ,對顯存的性能要求
訪問速度:極高的訪問速度
容量要求:滿足響應(yīng)分辨率和格式(RGBA崭捍,HDR等)的容量
功耗:電池供電設(shè)備的低功耗要求
在目前的移動設(shè)備硬件水平下尸折,這三個目標(biāo)通常最多只能滿足兩個。目前的主流選擇:犧牲容量殷蛇,換取更快的訪問速度和更低的功耗实夹。
2. 解決方案:TBR(Tile Based Rendering)
引入TileBuffer
2.1 SIMD 核心不直接寫到 FrameBuffer,而是寫到TileBuffer晾咪,TileBuffer 的內(nèi)容在恰當(dāng)?shù)臅r候?qū)懙?FrameBuffer
小容量收擦、高速 On chip 的 TileBuffer 作為 SIMD 的寫入目標(biāo)
FrameBuffer 會分塊依次渲染
單Tile上所有任務(wù),即每個 Tile 上的 DrawCall 都完成后才一次寫入顯存
TBR渲染流程
2.2 TBR 中的深度測試
TBR中的深度測試
- Early-Z:在 PS 之前執(zhí)行谍倦,測試失敗的像素不執(zhí)行PS
- Late-Z:在 PS 之后執(zhí)行塞赂,測試失敗的像素不寫入 Color Buffer
- 可見 Early-Z 能顯著降低 PS 的性能壓力,最好在無法應(yīng)用 Early-Z 的時候再啟用 Late-Z
什么情況下 Early-Z 失效昼蛀?
因為 Early-Z 在PS 之前執(zhí)行宴猾,所以需要必須在 PS 之前就確定深度,也就是說 執(zhí)行PS時像素深度不能發(fā)生變化
- Alpha Test / Clip:需要執(zhí)行完 PS 后叼旋,才能確定該像素深度是否被寫入
- Custom depth仇哆,PS中改寫深度值,硬件能夠感知夫植,Early-Z 階段無法獲取最終的深度值讹剔,失效。
2.3 移動端 GPU 的 Early-Z
移動端 GPU 的 Early-Z
- TileList 保存當(dāng)前 Tile 上的所有三角形列表
- 隱藏面剔除HSR(Apple/PowerVR), LRZ(Adreno), FPK(Mali)
- 原理:硬件層面先對 Tile 中的三角面進行 Depth Prepass详民,相當(dāng)于先進行了一邊深度測試延欠,剔除掉看不到的 fragment,可以顯著降低 overdraw 程度沈跨,減少 PS 調(diào)用次數(shù)
- 移動端 Early-Z 失效:打斷了硬件的 Depth Prepass
2.4 性能關(guān)注點
2.4.1 頂點開銷
- TBR 中頂點會存在 TileList 中由捎,過多的頂點會使得 TileList 過大,影響訪問性能和內(nèi)存開銷
- 移動端 Early-Z 雖然會降低 PS 開銷饿凛,但是會增加 VS 開銷(額外做一遍 Depth Prepass狞玛,有些廠商的GPU實際上是執(zhí)行了良次 VS,其中一次會優(yōu)化掉和位置計算無關(guān)的部分)涧窒,因此優(yōu)化 VS 復(fù)雜度(特別是位置計算)尤為重要
2.4.2 Tessellation
- 移動 GPU 中通常沒有專門的 Tessellation 單元心肪,效率更低
- Tessellation 產(chǎn)生更多頂點,需要執(zhí)行更多次 VS杀狡,增加開銷
2.4.3 充分利用 TileBuffer
TileBuffer 和主顯存之間消耗大量帶寬蒙畴,針對這個消耗進行如下的優(yōu)化
- 每一幀對 TileBuffer 執(zhí)行Clear ,避免上一幀的 Color Buffer/ Depth Buffer 又被從主存加載一邊
- 渲染完成后 Discard 掉不需要的 Render Target 或 Depth Buffer,避免會寫到顯存膳凝,節(jié)省大量帶寬碑隆。
3. TBDR:基于 TileBuffer 的延遲渲染
3.1 延遲渲染傳統(tǒng)流程
延遲渲染傳統(tǒng)流程
- 需要提供較多的顯存空間,支持多張 RT(MRT)蹬音,也就是GBuffer上煤,寫入材質(zhì)各種屬性
- 獨立的一個 LightPass,讀入每個像素的GBuffer屬性著淆,并進行光照處理劫狠,得到最終的 ColorBuffer
- 移動平臺上難以實現(xiàn)的原因:延遲渲染因為 MRT 的存在,大量消耗顯存讀寫的帶寬
3.2 移動設(shè)備上的延遲渲染(TBDR)
TBDR示意
- 像素的屬性(MRT)不被寫入主顯存永部,只存在于 TileBuffer中独泞,最終只復(fù)制 Color 信息到主顯存中,降低了帶寬消耗
- 幾乎不會占用額外帶寬
4. 移動設(shè)備上的 MSAA
4.1 傳統(tǒng) MSAA 流程
傳統(tǒng)MSAa
- 需要一個4倍RT來獲得4倍的fragment苔埋,最后再 resolve 會1倍的 RT
4.2 移動設(shè)備 MSAA 流程
移動端MSAA
- 多倍采樣的 RT 僅在 TileBuffer 中存在
- Resolve 發(fā)生在 TileBuffer 中懦砂,逐 Tile 執(zhí)行
- 僅單倍采樣的 RT 被寫回顯存
5. GPU 和 CPU 協(xié)作(Frame Pacing)
5.1 基本的 FramePacing 流程
FramePacing
- CPU 和 GPU 異步運行
- Command Buffer 作為 CPU 和 GPU 的協(xié)議包
- CPU 填充 CommandBuffer,在提交后(DrawCall)后 GPU 才開始執(zhí)行 CommandBuffer
5.2 對 FramePacing 進行優(yōu)化
目標(biāo):最大程度的并行化
- 盡早提交渲染命令组橄,讓 GPU 盡早開始工作荞膘,但避免將 CommandBuffer 拆分過于細碎,提交本身也有消耗
- 非渲染相關(guān)的 CPU 任務(wù)不必要等待上一幀渲染完成(不要等 GameUpdate)
- 保存數(shù)據(jù)單向流動(CPU到GPU)玉工,避免單幀內(nèi) CPU 依賴 GPU 執(zhí)行結(jié)果
CPU 幀內(nèi)依賴于 GPU 執(zhí)行結(jié)果的一個例子是 “硬件遮擋查詢”機制羽资,它需要 CPU 創(chuàng)建遮擋查詢命令提交給 GPU,等待 GPU 的運算結(jié)果遵班,再根據(jù)結(jié)果執(zhí)行渲染屠升。
5.3 垂直同步
渲染結(jié)果只能在固定的時間點顯示到屏幕,比如移動設(shè)備的屏幕刷新率為 60Hz狭郑,意思是手機屏幕每秒最多進行 60 次的緩沖區(qū)交換(渲染刷新)
問題:
- 若某幀渲染太快弥激,到了第一個同步點就刷新到屏幕了,而下一陣太慢愿阐,第二個同步點才刷新,則幀率不穩(wěn)定趾疚,有卡頓感
- 若刷新率慢而幀渲染率快缨历,未開啟垂直同步的情況下,可能畫面撕裂糙麦,上一個 FrameBuffer 才顯示一半辛孵,下一幀 FrameBuffer 已經(jīng)提交,再刷新就變成了下一幀的畫面
- 移動設(shè)備上始終開啟垂直同步赡磅,保證最小的幀間隔時間
- 做幀率限制時魄缚,保持最大幀率和屏幕刷新率整除的關(guān)系,如60Hz設(shè)備可限制 30FPS,20FPS冶匹,但不要限制 25FPS
- 使用圖形層 API 接口來限制