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...
減少著色器(管線)編譯造成的卡頓
警告
This page only applies to the Forward+ and Mobile renderers, not Compatibility. Ubershaders and pipeline precompilation rely on functionality only available in modern low-level graphics APIs (Vulkan, Direct3D 12, Metal). The Compatibility renderer uses OpenGL 3.3, OpenGL ES 3.0, or WebGL 2.0 depending on the platform. These versions lack the functionality to effectively implement ubershaders and pipeline precompilation.
To avoid shader stutters in Compatibility, you need to use the legacy approach of preloading materials, shaders, and particles by displaying them for at least one frame in the view frustum when the level is loading.
管線編譯,也常被稱為著色器編譯,是引擎為了讓 GPU 能繪製各種內容所需的一個高成本運算步驟。
Godot 中的著色器與材質,在能夠由 GPU 執行前,需要經過多個步驟處理。
更精確地說, 著色器編譯 指的是將 Godot 產生的 GLSL 程式碼轉換為跨平台可共用的中介格式(如 Vulkan 下的 SPIR-V)。然而,這個中介格式並不能直接由 GPU 使用。
管線編譯 則是 GPU 驅動程式將這個中介格式轉換成 GPU 真正可以用來繪製畫面的格式。驅動程式通常會在系統中某處快取這些管線,以避免每次遊戲執行時都要重新編譯。這個快取在驅動程式更新時通常會被刪除。
管線所包含的資訊不僅只有著色器程式碼,因此每個著色器可能會對應數十種甚至更多的管線!這讓引擎很難事先預編譯所有管線,因為這會非常耗時且佔用大量記憶體。此外,這個步驟只能在使用者的系統上執行,除非硬體和驅動版本完全相同,否則結果也難以在不同使用者間共用。
在 Godot 4.4 之前,除了當物件首次進入攝影機視野時才產生管線外,沒有其他解決方式,這導致著名的 著色器卡頓 ,通常只會在第一次遊玩時發生。自 Godot 4.4 起,引擎加入了新的機制來降低管線編譯導致的卡頓。
Ubershaders(超級著色器):Godot 利用 specialization constant(特化常數)這個功能,讓驅動程式能以光照、陰影品質等一組參數來最佳化管線程式碼。特化常數可用來限制不必要的功能,藉此最佳化著色器。每當更改特化常數時,就需要重新編譯管線。Ubershaders 是一種特殊版本的著色器,能在渲染時動態變更這些常數,代表 Godot 可以僅預先編譯一個通用管線,並在遊戲運作時於背景中編譯更優化的版本,大幅減少需建立的管線數量。
管線預編譯:藉由使用超級著色器,引擎能在多個時機點預先編譯管線,例如載入網格(Mesh)或新增節點到場景時。將其納入資源載入流程後,這些管線甚至可以在載入畫面或遊戲進行時,由多個背景執行緒並行預編譯。
從 Godot 4.4 起,Godot 會自動偵測所需的管線並在載入時預編譯。這套偵測機制大多是自動化的,但需要 RenderingServer 在載入階段能看到所有著色器、網格或渲染功能。舉例來說,如果你在遊戲執行時才載入某個網格和著色器,那麼這組合的管線直到載入時才會被編譯。同樣地,在遊戲運作時啟用 MSAA 或建立 VoxelGI 節點,也會觸發管線的重新編譯。
管線預編譯監控工具
預先編譯管線是 Godot 降低著色器卡頓的主要手段,但這並非萬全之策。瞭解可能導致管線卡頓的情境會很有幫助,且比起舊版 Godot,現在的解決方法也更為直接。隨著 Godot 未來版本導入更多偵測技術,這些臨時解法的重要性也會逐步降低。
Godot 除錯器提供了監控工具,可追蹤遊戲所建立的管線數量,以及觸發編譯的步驟。你可以在遊戲執行時觀察這些監控項目,無需每次都清除驅動快取就能找出潛在的著色器卡頓來源。若這些數值在非載入畫面時突然增加,表示首次遊玩時可能會出現卡頓。建議你務必檢查這些監控數據,幫助玩家避免卡頓,因為你自己若未刪除驅動快取或未在較弱硬體上測試,可能無法發現這些問題。
其中一個範例專案的管線編譯結果。
備註
我們可以看到遊戲進行時所編譯的管線,並確認哪些步驟可能會導致卡頓。請注意,這些數值只會增加而不會減少,因為這些監控工具不會追蹤已刪除的管線,而管線也可能在遊戲中被刪除並重新建立。
Canvas:在繪製 2D 節點時編譯。目前引擎尚未支援 2D 元素的預編譯,因此首次繪製 2D 節點時仍可能出現卡頓。
Mesh:在載入 3D 網格時會編譯,並判斷哪些管線能根據其屬性預編譯。如果在遊戲進行時載入網格,可能會導致卡頓,但若在背景執行緒載入則可減輕。節點上的材質覆蓋等修飾器無法在此步驟預編譯。
Surface:在準備繪製畫面時,首次將 3D 物件實體化到場景樹時編譯。這也可能包括尚未顯示的節點。只有節點第一次加入場景的那一幀會發生卡頓,如果發生在載入畫面後,通常不會造成明顯卡頓。
Draw: Compiled on demand when a 3D object needs to be drawn and an ubershader was not precompiled ahead of time. The engine is unable to precompile this pipeline due to triggering a case that hasn't been covered yet or a modification that was done to the engine's code. Leads to stutters during gameplay. This is identical to Godot versions before 4.4. If you see compilations here, please let the developers know as this should never happen with the Ubershader system. Make sure to attach a minimal reproduction project when doing so.
Specialization:於遊戲進行時於背景編譯,以最佳化影格率。這不會造成卡頓,但若每幀發生太多編譯,可能會降低 FPS。
管線預編譯功能
Godot 提供了許多渲染功能,但不一定每個遊戲都會用到。不過,管線預編譯無法預先判斷某功能是否會被專案使用。有些功能必須在使用者將節點加入場景或切換專案或環境設定時才會被偵測到。當這些功能首次被偵測到時,管線預編譯系統會記錄下來,往後建立的網格或表面就能預編譯支援這些功能的管線。
如果你的遊戲會用到這些功能,務必在載入大部分資產之前儘早準備一個會用到它們的場景。這個場景可以非常簡單,只要涵蓋遊戲計畫要用到的功能即可。如有需要,甚至可以讓它至少在螢幕外算繪一個影格,例如用 ColorRect 節點把畫面蓋住,或使用放在視窗邊界外的 SubViewport。
此外,遊戲進行時若變更這些功能,將會立即產生卡頓。請確保僅在設定畫面等必要時機才變更這些功能,並在套用變更時插入載入畫面與提示訊息。
MSAA 等級:當專案設定的 3D MSAA 等級變更時啟用。不幸的是,不同的視窗使用不同 MSAA 等級時會造成卡頓,因為引擎一次只追蹤一個等級以進行預編譯。
反射探針:在場景中加入 ReflectionProbe 節點時啟用。
獨立高光:當使用次表面散射或需直接從螢幕取樣高光的合成特效時啟用。
動態向量:當使用 TAA、FSR2 或需要動態向量(如動態模糊)的合成特效時啟用。
法線與粗糙度:當使用 SDFGI、VoxelGI、螢幕空間反射、SSAO、SSIL,或自訂著色器 / CompositorEffect 使用
normal_roughness_buffer時啟用。光照貼圖:當場景中加入 LightmapGI 節點並有節點使用烘焙光照貼圖時啟用。
VoxelGI:場景中加入 VoxelGI 節點時啟用。
SDFGI:WorldEnvironment 啟用 SDFGI 時啟用。
多重視角:XR 專案時啟用。
16/32 位元陰影:當專案設定更改陰影貼圖的深度精度時啟用。
全向光雙拋物面陰影:當全向光源啟用陰影並使用雙拋物面模式時啟用。
全向光立方體陰影:當全向光源啟用陰影並使用立方體貼圖模式(預設)時啟用。
如果你在遊戲進行時遇到卡頓,且監控工具顯示 Surface 步驟的編譯數量突然增加,很可能是某項功能在事前未啟用。請確保這些效果在遊戲載入時就被啟用,以減輕相關問題。
管線預編譯與實體化
遊戲中常見的卡頓來源之一,是某些效果只有在特定互動發生時才會動態實體化到場景。例如,若有一個粒子特效,只有玩家執行某個動作時才用腳本加到場景。即使該場景已預先載入,若效果從未實體化過,引擎仍可能無法預先編譯對應的管線。
所幸,從 Godot 4.4 起,只要場景被實體化過一次(即使完全不可見或在攝影機外),這些管線就能被預先編譯。
在某範例專案中,將隱藏的子彈節點掛在玩家物件下。這樣有助於引擎預先編譯該特效所需的管線。
如果你知道某些特效會在遊戲過程中動態加入場景,且發現這些特效出現時編譯數量會突然增加,一個解決方法是將這些特效的隱藏版本掛載在場景內某處,確保它們一定會被實體化一次。
舉例來說,若玩家角色能引發爆炸效果,你可以將該特效作為隱藏節點掛載在玩家物件下。請記得停用隱藏節點上的腳本,或隱藏可能造成問題的其他子節點,這可透過啟用節點的 可編輯子節點 選項來達成。
著色器烘焙器
自 Godot 4.5 起,你可以選擇在匯出時烘焙著色器,以改善初次啟動時間。這通常無法解決既有的卡頓,但能縮短遊戲首次載入所需時間。特別是在使用 Direct3D 12 或 Metal 時,因需要轉換步驟,初次編譯著色器的時間會明顯慢於 Vulkan。Godot 自身的著色器使用 GLSL 與 SPIR-V,而 Direct3D 12 與 Metal 則使用不同格式。
備註
著色器烘焙器只能將來源烘焙為中介格式(Vulkan 為 SPIR-V、Direct3D 12 為 DXIL、Metal 為 MIL)。無法將中介格式烘焙為最終管線,因為該步驟取決於 GPU 驅動程式與硬體。
著色器烘焙器不是管線預編譯的替代方案,而是作為補充。
啟用後,著色器烘焙器會將已編譯的著色器程式碼打包進 PCK,以完全略過執行時的編譯步驟。缺點是匯出時間會稍長,且 PCK 檔案會增大數 MB。
著色器烘焙器預設為停用。你可在匯出對話框的各個匯出預設中勾選 Shader Baker > Enabled 來啟用。
請注意,著色器烘焙僅能匯出目前編輯器執行平台所支援驅動的著色器:
在 Windows 執行的編輯器可匯出 Vulkan 與 Direct3D 12 的著色器。
在 macOS 執行的編輯器可匯出 Vulkan 與 Metal 的著色器。
在 Linux 執行的編輯器只能匯出 Vulkan 的著色器。
在 Android 執行的編輯器只能匯出 Vulkan 的著色器。
著色器烘焙器僅會匯出與目標平台之專案設定 rendering/rendering_device/driver 相符的著色器。
備註
著色器烘焙器僅支援 Forward+ 與 Mobile 算繪器。若專案使用 Compatibility 算繪器,或因硬體不支援 Forward+/Mobile 而使用 Compatibility 後備,將不會有任何效果。
這也代表網頁平台不支援著色器烘焙器,因為該平台僅支援 Compatibility 算繪器。
Additionally, the shader baker is not supported when exporting a project
using the --headless command line argument,
as Godot cannot access the GPU when running in headless mode.