Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
GPU 最佳化
前言
對於新圖形功能與進步的需求,幾乎可以確定你會遇到圖形瓶頸。有些瓶頸可能發生在 CPU 端,例如 Godot 引擎內部為算繪物件所做的計算。有時瓶頸也可能出現在圖形驅動程式的 CPU 部分,負責整理並傳遞指令給 GPU,或是指令傳輸過程中。最終,瓶頸也會發生在 GPU 本身。
算繪時瓶頸出現的位置高度依賴於硬體。特別是行動裝置上的 GPU,可能會對於桌面端能輕鬆運作的場景感到吃力。
理解並分析 GPU 瓶頸與 CPU 上的情況略有不同。這是因為你通常只能透過改變給 GPU 的指令來間接調整效能。此外,量測起來也較困難。在許多情況下,唯一能衡量效能的方式就是觀察每個影格的算繪時間變化。
繪圖呼叫、狀態切換與 API
備註
以下內容雖然與終端使用者無直接關聯,但能協助理解後續章節所需的背景知識。
Godot 透過圖形 API(Vulkan、OpenGL、OpenGL ES 或 WebGL)向 GPU 發送指令。這些溝通與驅動處理會消耗不少資源,特別是在 OpenGL、OpenGL ES 與 WebGL 上。如果我們能以驅動程式和 GPU 最佳化的方式給予指令,則可以大幅提升效能。
在 OpenGL 中,幾乎每個 API 指令都必須做一定的驗證,以確保 GPU 處於正確狀態。即使是看似簡單的指令,也會引發大量幕後的管理作業。因此,我們的目標是將這些指令減至最低,並盡可能將相似的物件分組,以便一起算繪,或以最少的昂貴狀態切換完成。
2D 批次處理(Batching)
在 2D 中,逐一處理每個項目的成本可能高得難以負擔——畫面上很容易就有上千個項目。這就是為什麼要使用 2D 的 批次處理(Batching)。多個相似的項目會被分組成一個批次,透過單一繪圖呼叫(draw call)一起算繪,而不是為每個項目分別呼叫。此外,這也能將狀態切換、材質與貼圖的變更降到最低。
3D 批次處理(Batching)
在 3D 算繪中,我們同樣希望減少繪圖呼叫與狀態切換。不過,將多個 3D 物件合併成單一繪圖呼叫會更困難。3D 網格通常包含數百到數千個三角形,實時合併大型網格的成本極高。隨著每個網格三角形數增加,合併的效益很快就被成本抵銷。一個更好的做法是**事先將網格合併**(即靜態物件之間的合併),可以由美術人員在 DCC 工具中完成,也可在 Godot 內透過外掛程式自動化。
在 3D 中進行批次合併也有代價。多個物件合併為一次算繪後,無法個別進行遮蔽剔除(culling)。若螢幕上的一片草與螢幕外的一整座城市被合併,城市仍會被算繪。因此在嘗試將 3D 物件批次合併時,務必考量物件位置與剔除。儘管如此,合併靜態物件的效益在多數情況下仍高於其缺點,尤其是對大量遠距或低多邊形物件。
如需更多 3D 特定最佳化資訊,請參考 3D 效能最佳化。
重複利用著色器與材質
Godot 的算繪器設計與其他引擎略有不同,目標是盡量減少 GPU 狀態切換。 StandardMaterial3D 能在需要相似著色器時有效重複利用材質。如果你使用自訂著色器,也請儘可能進行重複利用。Godot 的優化優先順序如下:
重複利用材質: 場景中材質種類越少,算繪速度就越快。如果場景中物件數量龐大(數百到數千),應盡可能共用材質。最糟情況下,可使用圖集(atlas)來減少紋理切換次數。
重複利用著色器: 如果材質無法共用,至少要嘗試共用著色器。注意:只要 StandardMaterial3D 設定相同(例如核取方塊開關的功能),即使參數不同,Godot 也會自動共用同一份著色器。
假設一個場景有 20,000 個物件、每個物件都用不同材質,算繪效能會非常低。如果同樣的場景只有 100 種材質共用,算繪就會快許多。
像素成本 vs 頂點成本
你可能聽說過模型的多邊形數越少,算繪速度就越快。這事實上只是 相對而言,還牽涉許多其他因素。
在現代 PC 與主機上,頂點成本其實很低。早期的 GPU 只算繪三角形,這意謂著每個影格:
所有頂點都必須由 CPU 處理(包括裁切/clipping)。
所有頂點都必須從主記憶體傳送到 GPU 記憶體。
現在這一切都在 GPU 內部處理,效能大幅提升。3D 美術常誤以為多邊形數會嚴重拖慢效能,是因為 3D 建模軟體(如 Blender、3ds Max 等)為了編輯方便,必須將幾何資料留在 CPU 記憶體,導致效能較差。遊戲引擎則大多將運算交由 GPU,因此可更高效地算繪大量三角形。
在行動裝置上則不同。PC 和主機的 GPU 可以無限制地從電網取得電力,屬於高效能型;而行動 GPU 受限於小型電池,必須極度講究效能與耗能。
為了提升效率,行動 GPU 會盡量避免 重複繪製(overdraw)。所謂 overdraw,是指螢幕上同一像素被多次算繪。例如一個小鎮有多棟建築,GPU 無法預先知道哪些遮擋哪些,必須先算繪後判斷。假設一棟房子先被畫出來,後又有另一棟房子在前方遮住,導致同一像素被算繪兩次。PC GPU 通常不太在意這點,只要加更多像素處理器來彌補(但會消耗更多電力)。
在行動裝置上無法無限制用電,因此大多採用 圖塊式算繪(Tile-based rendering) 技術,將螢幕劃分為網格,每個格子會記錄哪些三角形要繪製,並根據深度排序以減少 overdraw。這種技術提升了效能並降低功耗,但會犧牲頂點處理效能,導致每次可算繪的頂點與三角形數量變少。
此外,圖塊式算繪在螢幕局部有大量幾何細節的小物件時效率會大幅下降。這會造成單一圖塊負載過重,其他區域必須等待該圖塊算繪完成才能顯示影格,導致整體效能降低。
總結來說,在行動裝置上不用太擔心頂點總數,而是要**避免頂點過度集中在螢幕某一小區域**。若角色、NPC、車輛等距離很遠(在螢幕上很小),應降低 LOD(細節層級)。即使在桌面端,也避免讓三角形小於螢幕一個像素的大小。
使用以下功能時,請留意額外的頂點運算成本:
蒙皮(骨骼動畫)
變形(形狀鍵)
頂點光照物件(行動裝置常見)
像素/片元著色器與填充速率(Fill Rate)
相較於頂點運算,片元(像素)著色的成本近年來大幅提升。螢幕解析度從 640×480 VGA(僅 307,200 像素)躍升到 4K(8,294,400 像素),面積暴增 27 倍!加上現代物理式算繪(PBR)導致片元著色器極度複雜,每個像素都需繁複運算。
你可以很容易測試專案是否受填充速率限制。請先關閉 V-Sync(避免 FPS 被上限限制),然後比較大視窗與極小視窗下的 FPS。如果有用到陰影,試著減小陰影貼圖尺寸。通常你會發現小視窗時 FPS 大幅提升,代表你受限於填充速率;若提升有限,瓶頸則在其他地方。
You can increase performance in a fill rate-limited project by reducing the amount of work the GPU has to do. You can do this by simplifying the shader (perhaps turn off expensive options if you are using a StandardMaterial3D), or reducing the number and size of textures used. Also, when using shaded particles, consider forcing vertex shading in their material to decrease the shading cost.
也參考
在支援的硬體上,可利用 可變速率著色 降低著色運算成本,同時又不會影響最終影像的邊緣銳利度。
針對行動裝置開發時,建議盡可能使用最簡單的著色器。
紋理讀取
片元著色器的另一個成本在於紋理讀取。讀取紋理本身就很耗效能,若在單一著色器中讀取多張紋理則成本更高。另外,紋理過濾(如 mipmap 間的三線性過濾與平均)也會進一步拖慢速度。對於行動裝置,紋理讀取除了效能外,也會顯著增加耗電。
如果你使用第三方著色器或自行撰寫著色器,請盡量選擇需要最少紋理讀取的演算法。
紋理壓縮
Godot 預設在匯入 3D 紋理時會採用顯示記憶體(VRAM)壓縮。這種壓縮儲存體積雖然不如 PNG 或 JPG 有效,但在繪製大型紋理時能大幅提升效能。
這是因為紋理壓縮的主要目標是減少記憶體與 GPU 間的頻寬消耗。
在 3D 算繪中,物件的形狀主要取決於幾何形狀,因此壓縮對畫面影響較小;而在 2D,紋理內的細節與形狀較重要,所以壓縮產生的失真與雜訊會更明顯。
請注意,大多數 Android 裝置不支援帶透明度的紋理壓縮(僅支援不透明),請務必留意。
備註
即使在 3D 場景下,「像素美術」紋理也應停用 VRAM 壓縮,因為壓縮會明顯影響畫面品質,而低解析度本身並不會帶來顯著效能提升。
後製處理與陰影
後製特效與陰影在片元著色階段同樣非常耗資源。務必在不同硬體上測試這些效果對效能的影響。
降低陰影貼圖(shadowmap)大小能明顯提升效能,無論是寫入還是讀取都一樣。此外,最佳化陰影效能最有效的方法,就是盡量減少有陰影的燈光與物件。對於較小或較遠的全域光(OmniLight)/聚光燈(SpotLight),通常可以直接關閉陰影而對畫面影響極小。
透明與混合
透明物件對算繪效能有特殊挑戰。不透明物件(特別在 3D 裡)可以用任何順序算繪,Z 緩衝區會自動保證最前面的物件被正確著色;但透明或混合物件則無法用 Z 緩衝排序,必須改用「畫家演算法」(painter's order,從後往前繪製)才能正確顯示。
透明物件對填充速率的影響也特別嚴重,因為每個圖層都要被算繪,即使後面還會被其他透明物件覆蓋。
不透明物件則不會有這個問題,因為可以利用 Z 緩衝區,先寫入 Z 值,再只對最前方(勝出)的像素進行片元著色器運算。
當多個透明物件重疊時,效能成本尤其高。通常,應盡量縮小透明區域以減低填充速率需求,這點在行動裝置上尤其重要。許多情況下,直接算繪較複雜的不透明幾何體,反而比用透明材質「取巧」來得有效率。
跨平台建議
如果你計畫跨平台發行,請務必 及早 、 頻繁 地在所有目標平台進行測試,特別是行動裝置。只在桌面端開發,最後才臨時移植到行動裝置,幾乎注定會遇到災難。
一般來說,應以最低通用規格設計遊戲,再針對高階平台額外加入進階特效。例如,若同時支援桌面與行動平台,可考慮都採用「相容性算繪模式」來確保效能。
行動裝置/圖塊式算繪器
如前所述,行動裝置的 GPU 運作機制與桌機大不相同。多數行動裝置採用圖塊式算繪器(Tile Renderer),將螢幕劃分為固定大小的區塊並存入快取,以減少對主記憶體的讀寫次數。
但圖塊式算繪也有缺點,某些技術會變得更複雜且低效。若圖塊之間需要互相依賴(如跨圖塊運算)或必須保留前一輪算繪結果,效能會大幅降低。因此,請務必仔細測試著色器、視窗紋理與後製效果在行動裝置上的效能表現。