Mobile GPU Tile Based Rendering 介紹

River Wang
二流遊戲開發
Published in
14 min readDec 5, 2021

今年看完 Mali GPU Training Series 後終於對於 Mobile GPU 架構與 Tile Based Rendering (翻譯成基於瓦塊單位的繪圖?) 有更完整的認識。
我還是想寫個介紹專文給"所有想學”的人,包含非資訊工程背景的人 (美術專業/企劃專業/電腦圖學新手/路人/…),所以這篇還是盡量寫中文解釋為主。

Graphics Rendering Pipeline

CPU 到 GPU 之間協力實現電腦圖學 (Computer Graphics) 的繪圖管線流程工作,讓使用者/玩家可以看到開發者呈現的 2D/3D 畫面,需要將繪圖資源 (Resource) 與繪圖工作 (Command) 傳遞到 GPU 端。傳統的中/低階電腦可以期待有 100~150+ Watts 的電源供應,然而移動裝置 (Mobile) 因為普遍沒有冷卻設備 (冷卻風扇/銅管),所以必須在考慮極端節省電源功耗的前提下讓 CPU/GPU 工作,因而限制了移動裝置普遍只能使用 2~3+ Watts 的電源供應。在如此極端的前提下,Tile Based Rendering 架構成為 Mobile GPU 的解決方案與趨勢。

先介紹”傳統”電腦圖學的繪圖管線工作流程 (Graphics Rendering Pipeline)。

Coordinate System: 空間座標系
Transformation: 準備一個 4x4 轉置矩陣,用矩陣乘法實現不同空間座標系的轉換
GPU Rasterization 填滿像素工作的基本對象是一個三角面

Object |-> World |-> View/Camera |-> Clip |-> Screen Output

首先在軟體應用程式 (遊戲/繪圖軟體) 會準備好繪圖資源或場景,對一個幾何物件來說一開始幾何資訊都定義在物體空間 (Object Space)。
然後由軟體端定義好場景世界空間,以及物件相對於場景世界的座標,要得到該正確的世界座標所需要的轉換步驟會推導成 Modeling Transformation 矩陣,然後繪圖過程中我們可以計算出正確的世界座標 (World Space Coordinate)。
接下來軟體也要為這次繪圖定義一個可視範圍,在電腦圖學中稱之為鏡頭 (Camera)。為了接下來投影範圍計算,所以我們必須計算出鏡頭空間座標 (Camera Space Coordinate,以該鏡頭為此空間的原點 [0, 0, 0]),這時需要軟體針對目前定義的鏡頭推導出一個從世界空間轉換成鏡頭空間的 Viewing Transformation 矩陣。

圖中範圍標示不正確,我跳過 Homogeneous Division 步驟,才能得到這個正規投影空間,並且左下角最小範圍是 [-1, -1, 0],右上角最大範圍是 [1, 1, 1]。左邊 Clip Space Coordinate [0, 0, 0] 可對應到右邊 Screen Cooridnate [500, 500] (螢幕中心點)

再來軟體同樣先推導出一個 Projection Transformation 投影矩陣 (有分平行投影與透視投影,但此篇不談這部分),將鏡頭空間轉換至正規投影空間 (Clip Space Cooridnate,其實我解釋的空間是 Canonical Space,但我跳過了一些重要細節,謝謝網友指正)。所謂的正規投影空間以螢幕中心為 XY 平面原點 [0, 0, 0],螢幕範圍最小值是 [-1, -1, 0],而最大值則是 [1, 1, 1] 的空間。正規投影空間的主要目的是定義 2x2x1 的立方體為可視與投影範圍,方便硬體判斷繪圖目標是否超出可視範圍,如果在範圍內,X與Y座標方便轉換成螢幕座標找到正確要處理的螢幕像素,而Z座標則代表與鏡頭間的深度資訊。
最後一步 Rasterization (光柵化…我一直覺得這個中文翻譯很差…),硬體已經知道三角面中每個頂點的正規投影座標,非常智慧地找出此三角面覆蓋到的所有像素 (Pixels),將正確的顏色資訊填入,完成螢幕顏色填滿工作。

現在回頭來檢視這個繪圖流程,繪圖資源的來源當然是 CPU 端,而同時每次繪圖會用到的轉置矩陣 (Modeling/Viewing/Projection Transformation) 也是由 CPU 先計算好,然後傳遞給 GPU 端。GPU 對於要一個繪圖工作 (Draw Call),它最原始的輸入資料便是定義在物體空間中的三角面頂點們,然後透過以上轉置矩陣計算出正確的正規投影座標,以便接下來 Rasterization 接手。故,在各引擎/繪圖軟體中 Shader Codes 常看到的 MVP(_Matrix) 變數其實就是 Model_Transformation * View_Transformation * Projection_Transformation 矩陣相乘結果。

Clip-Space-Pos = MVP_Transformation_Matrix * Object-Space-Pos;

GPU Rendering Pipeline

接著從 GPU 工作的角度來介紹繪圖管線流程。分成兩大階段,幾何處理工作 (Geometry Processing) 與像素處理工作 (Pixels/Fragments Processing)。

這裡已經從 GPU 的角度說起,自然牽涉到 Shaders (著色器…我也不太喜歡這翻譯 XD) 運作了,幾何處理工作階段是由 Vertex Shader (頂點著色器) 負責處理,像素處理工作階段則是 Fragment/Pixel Shader (像素著色器…Fragment = Pixel,很多地方在介紹時因為作者習慣有這兩種說法,其實就是同一件事) 擔當要角。

GPU 進行繪圖工作時,輸入的幾何資料基本上是一或多個三角面,對每個三角面的每個頂點,平行化去執行各自的 Vertex Shader,Vertex Shader 需要計算該頂點最終的正規投影座標。過程中 Vertex Shader 被允許存取外部傳入資料如貼圖資源、外部變數參數等。
GPU 可自動排除不該出現的背向三角面 (Culling)。
正規投影空間中,GPU 可以判斷出該三角面是否有出現在可視範圍內 ([-1, -1, 0] ~ [1, 1, 1]),如果三角形與正規可視範圍邊界有交集的話,GPU 會自動做必要的 Clipping 與新的三角形重組。完全在可視範圍外的三角面自然被排除,不再進行下一階段工作。

這裡不詳談如何填滿三角面的演算法,快速用一張圖簡單解釋 Rasterization 在做什麼。GPU 自動找出覆蓋到的像素後,每個像素各自平行執行 Fragment Shader 計算顏色結果

像素處理階段除了 Fragment Shader 之外,還有幾個 GPU 固定硬體模組十分重要,它們非 Shader 可操控,因此被稱為 Fixed Function (固定硬體功能)。

借 Arm Mali GPU Training 內容解釋 Pixel Processing
此三角面實際覆蓋 10 個 Pixels,但呼叫了 20 次 Fragment Shaders

第一步 Rasterizer 自然負責 Rasterization 工作,找出所有被覆蓋的像素。有一點需要注意的是 Fragment Shader 其實以 2x2 為一組的 Pixels 一起被呼叫的,所以每次處理到的像素最小單位其實是 2x2 Pixels。
第二步 ‘Eary ZS (深度/Stencil) Test’ 通過深度測試與 Stencil 測試 (Stencil 遮罩功能,此篇不談) 檢查被遮擋的像素,不通過的自然被排除不再參與接下來的繪圖工作。
第三步才是執行 Fragment Shader 工作,在此開發者同樣存取外部繪圖資源 (貼圖、外部變數等) 進行計算顏色結果。如果 Fragment Shader 會改變深度資訊,會造成第二步的判斷不可靠,因此 GPU 會放棄第二步的深度/Stencil測試,繪圖效能較低。
第四步 ‘Late ZS Test’ 在 Fragment Shader 之後會再做一次深度/Stencil測試,如果該像素會被遮擋排除,應盡可能保持該像素在第二步時被排除。
最後一步 ‘Blend’ 負責透明顏色的合成計算,將螢幕像素上已存在顏色 (上一次繪圖填入,Destination Color)與這次 Fragment Shader 計算出來的透明顏色 (Source Color) 依照指定的公式計算合成結果。
最後的顏色結果會寫入到指定像素對應的記憶體區塊 (常被稱為 Frame Buffer),完成該像素顏色更新。

Arm Mali GPU 提到在像素處理功能上可做以下優化:GPU 實現 Hidden Surface Removal 功能 (不用參考繪圖順序自動排除所有被遮擋像素,此篇不詳談此演算法),並且將 Fragment Shader 與 ‘Late ZS Test’ 功能整合,讓 Fragment Shader 可主動及早觸發 ‘Late ZS Test’,更快地跳過不必要的剩餘 Fragment Shader 運算工作。

Hidden Surface Removal 這篇不談 (我也還不會解釋 XD)

聽說這篇要談 Tile Based Rendering … XD
好的以上對於 GPU 實現的電腦圖學繪圖管線流程做了初步的說明,那麼對移動平台 (手機/平板/智慧電視/…) 來說問題在哪裡?一開始提到的省電!

Memory Access

先從 CPU 的角度談,一開始執行程式時,所有必要的資料都要從硬碟搬到主記憶體上 (Main Memory/DRAM) CPU 才看的到那些資料,但 CPU 要運行時想拿資料最快的方式是把必要資料從主記憶體搬到自己身上,也就是快取記憶體 (Cache)。用一個例子比喻的話就是你在家念書時你可以看到的書都在書櫃上 (DRAM),但你要拿到書最快的方式就是把書搬到你身旁 (書桌 / Cache)。

GPU 不是 CPU,雖然也有快取記憶體用來存放必要資料 (繪圖資源屬性 / 貼圖 / Shader 外部變數參數 / 繪圖指令 / Shader 程式指令 / …),但一定比 CPU 可放的容量小很多,並且 GPU 存取 DRAM 的速度效率也比 CPU 慢非常多。故當我們要做全螢幕繪圖工作時,會發現 GPU 必須能看的到全螢幕上所有像素 (1920x1080 = 2073600 Pixels),很明顯 GPU 的快取記憶體放不下,那如果還是執意要 GPU 的 Rasterizer 每遇到一個三角面就對全螢幕工作的話,就是逼 GPU 要隨時跑回去存取 DRAM,這樣存取速度效率以及所需要的電源功耗消耗,就不是移動裝置 (2–3+ 瓦電源功耗) 可以承受得了的。

Tile Based Rendering 終於?

現在想像自己是 GPU 中某一個運作核心,你現在在教室裡上課,有一個任務要把教室大黑板 (定義成 1920x1080 個格子) 填滿指定的顏色,因為你一次只能填一格顏色,所以如果你要對全螢幕工作的話,會發現你無法把大黑板搬到你的桌上讓你做事,你只能走過去大黑板畫上一格之後再走回自己座位,這樣不停重覆 (消耗較多脂肪…)。

因此換個方法,以 32x32 個格子視為一個瓦塊單位 (Tile) 去切割整個大黑板,切成 60 x 34 個瓦塊 (有機會超出原本工作格子)。接下來你要指定要填入哪一格甚麼顏色有同學會跟你講好,但你坐在位置上不用動,有部分同學的工作是專門跑去黑板上把指定的瓦塊 (32x32) 內容抄下來,然後把那一小塊搬到你桌上讓你做事,你仍然一次只能填入一格顏色,但寫入一格後就接著寫下一格,你需要的東西都在你桌上。當這個瓦塊工作全做完後,其他同學就會把瓦塊再搬過去大黑板,把你填入的內容抄回去大黑板上。

以上的比喻就是 Tile Based Rendering 的基本精神。GPU Rasterizer 每次工作的對象換成一個瓦塊單位 (16x16/32x32/64x64 Pixels)。在這之前的幾何工作階段,GPU 會等待所有的三角面都處理完所有的頂點,然後運算找出每個瓦塊 (Tile) 中包含到哪些三角面,因此之後 GPU Rasterizer 在處理 Tile 時已知有多少個三角面要在此進行 Rasterization,並同時可對多個三角面進行 Hidden Surface Removal 處理。這樣一個 Tile Rasterization 工作所需要的資料包含所有已知三角面幾何資料 (Triangles)、有參與之其他繪圖資源 (貼圖等)、對象瓦塊 (EX: 32x32 Pixels),這些資料為一整組工作資料集合 (Working Set),足夠被放在 GPU 快取記憶體中,讓 GPU 運行存取資料時達到最高效率,也最省電!

在 Arm Mali GU Training Series Ep 1.3 當中有展示一段動畫來解釋傳統立即刷新全螢幕 (Immediate Renderer,1分35秒) 與瓦塊單位刷新繪圖 (Tile Based Renderer,2分58秒) 行為上的差別。

最後以我自己的理解,畫一個符合 Tile Based Rendering 的 GPU Rendering Pipeline。

第一階段是幾何處理階段,依序處理所有輸入的三角形,找出所有瓦塊 (Tile) 對應的三角形資料;然後才進入第二階段像素處理階段,依序處理所有瓦塊 (Tiles),先將螢幕 (Frame Buffer in DRAM) 上該瓦塊對應的所有像素複製一分到瓦塊工作區 (Tile Working Set in GPU Cache) 裡,然後交給 Rasterizer 開始正常做像素處理工作 (Hidden Surface Removal |-> Early Z/Stencil Test |-> Fragment Shader |-> Late Z/Stencil Test |-> Blend),計算出瓦塊中每個像素顏色結果後,再將瓦塊結果複製寫回去螢幕資料。

--

--

River Wang
二流遊戲開發

學生時代就跳入 Computer Grpahics 領域,其他技術好像都不想學/學不來,工作也找 Graphics 相關內容,但幾年後第二份工作才真的投入遊戲產業,不過也是個失敗的經驗。目前雖然逃離遊戲慘業,但也沒混出什麼出息,仍然在混口飯吃,並幻想著某天自己終於有時間開始完成自己想做的遊戲作品。