Unity-異步著色器編譯
異步著色器編譯可以在編譯新的著色器變體時(shí)渲染虛擬著色器,從而防止 Unity Editor 停頓。默認(rèn)情況下,在 Editor 設(shè)置(菜單:__Edit > Project Settings…> Editor > Shader Compilation__)中已啟用異步著色器編譯。
著色器可以包含成百上千個(gè)變體,這些變體涵蓋具有不同關(guān)鍵字組合的不同使用場(chǎng)景。如果在加載著色器時(shí) Editor 必須編譯所有變體,則著色器導(dǎo)入過程將非常緩慢。 為了處理此問題,Unity 在默認(rèn)情況下使用_按需著色器編譯。這意味著 Editor 在場(chǎng)景中首次遇到著色器變體時(shí)會(huì)編譯這些著色器變體。 這種情況下會(huì)使 Editor 中的渲染停頓,因?yàn)橥暾闹骶幾g可能需要數(shù)毫秒到數(shù)秒的時(shí)間。編譯著色器變體所需的時(shí)間取決于所選的圖形 API 和著色器的復(fù)雜性。要克服這些停頓問題,請(qǐng)使用__異步著色器編譯_。
在 Game 視圖和 Scene 視圖中禁用異步著色器編譯
默認(rèn)情況下已啟用異步著色器編譯??梢葬槍?duì) Game 視圖和 Scene 視圖禁用此功能。如果不知道渲染解決方案的哪些部分導(dǎo)致了問題,這樣做將很有用。但是,在著色器編譯期間,可能仍然會(huì)在 Editor 中遇到渲染停頓的情況。
要禁用異步著色器編譯,請(qǐng)執(zhí)行以下操作:
1) 在項(xiàng)目中,選擇?Edit > Project Settings…> Editor。
2) 在 Editor 設(shè)置底部的?Shader Compilation?下,取消選中?Asynchronous Shader Compilation?復(fù)選框。

注意:禁用異步著色器編譯后,該設(shè)置僅影響 Game 視圖和 Scene 視圖。該設(shè)置不會(huì)影響顯式啟用異步著色器編譯的系統(tǒng)和自定義腳本。
異步著色器編譯的工作原理
通過異步著色器編譯,Editor 可以將著色器變體添加到編譯隊(duì)列中,并在作業(yè)線程中完成這些著色器變體,而不會(huì)立即編譯這些變體。.為了避免渲染停頓,Editor 在編譯著色器變體的同時(shí)繼續(xù)進(jìn)行渲染。然后,Editor 使用純青色的虛擬著色器來填充著色器變體的位置(偶爾會(huì)短暫看到青色虛擬著色器,顯示后臺(tái)正在進(jìn)行的操作)。這樣可避免因?yàn)?Editor 仍在編譯某些對(duì)象而未在場(chǎng)景中渲染這些對(duì)象。編譯完成后,Editor 將換入真實(shí)著色器。Editor 右下角的進(jìn)度條會(huì)顯示編譯隊(duì)列的狀態(tài)。
該功能對(duì)獨(dú)立平臺(tái)播放器沒有任何影響,因?yàn)?Editor 會(huì)在構(gòu)建過程中編譯播放器所需的所有著色器變體。
注意:默認(rèn)情況下,異步著色器編譯功能在 Game 視圖和 Scene 視圖中有效。如果希望將此功能用于其他情況,請(qǐng)參閱在自定義 Editor 工具中使用異步著色器編譯。

注意:如果使用了?DrawProcedural
?或?CommandBuffer.DrawProcedural
,Editor 不會(huì)換入虛擬著色器。Editor 只是跳過對(duì)此著色器變體的渲染,直到此著色器變體的編譯完成為止。
注意:Blit 操作不會(huì)使用異步著色器編譯。這是為了保證在最常見的用例中獲得正確輸出。
Editor 中的高級(jí)渲染
默認(rèn)情況下,異步著色器編譯功能在 Scene 視圖和 Game 視圖中有效。高級(jí)渲染解決方案依賴于一次生成數(shù)據(jù)并在以后的幀中進(jìn)行復(fù)用,這可能會(huì)導(dǎo)致虛擬著色器污染已生成的數(shù)據(jù)。如果發(fā)生此情況,即使在著色器完成編譯后,也可能會(huì)在場(chǎng)景中看到青色或其他渲染瑕疵。只有在 Editor 第一次遇到生成數(shù)據(jù)的著色器變體時(shí)才會(huì)發(fā)生此問題。但是,為了避免這些問題,可以針對(duì)渲染的某些部分禁用異步著色器編譯,對(duì)某些著色器強(qiáng)制進(jìn)行同步著色器編譯,或者在編譯完成后檢測(cè)特定的數(shù)據(jù)污染情況并重新生成數(shù)據(jù)。
在特定渲染調(diào)用中禁用異步著色器編譯
如果要使用異步著色器編譯,但又不希望虛擬著色器在特定渲染調(diào)用中影響渲染結(jié)果,則可以在 C# 腳本中針對(duì)這些調(diào)用禁用此功能。以下說明展示了如何在直接作用域和 CommandBuffer 作用域中禁用該功能。在這兩種情況下,實(shí)際上就是在不想讓 Unity 異步編譯的渲染調(diào)用周圍注入鉗制。
在直接作用域中
在_直接作用域_中,必須使用?ShaderUtil.allowAsyncCompilation;
。
將?ShaderUtil.allowAsyncCompilation
?的當(dāng)前狀態(tài)保存到變量。 在調(diào)用要禁用異步編譯的渲染命令之前,立即將ShaderUtil.allowAsyncCompilation
?設(shè)置為?false
。 在希望影響的渲染調(diào)用之后,將?ShaderUtil.allowAsyncCompilation
?恢復(fù)到步驟 1 中保存的前一個(gè)狀態(tài)。
下面是一個(gè)偽代碼示例:
在 CommandBuffer 作用域中
在?CommandBuffer?作用域中,必須使用?ShaderUtil.SetAsyncCompilation
?和?ShaderUtil.RestoreAsyncCompilation
。
在調(diào)用要禁用異步編譯的渲染命令之前,立即調(diào)用?ShaderUtil.SetAsyncCompilation
?并將其設(shè)置為?false
。緩沖區(qū)中所有緊隨其后的命令不會(huì)允許異步編譯。 在希望影響的渲染命令之后,使用?Shader.Util.RestoreAsyncCompilation
。
下面是一個(gè)偽代碼示例:
針對(duì)特定著色器強(qiáng)制進(jìn)行同步編譯
可以針對(duì)特定著色器強(qiáng)制進(jìn)行同步編譯。如果生成數(shù)據(jù)的著色器在渲染開始時(shí)始終存在且編譯速度相對(duì)較快,這是一個(gè)很好的選擇。
要針對(duì)特定著色器強(qiáng)制進(jìn)行同步編譯,請(qǐng)執(zhí)行以下操作: 在著色器代碼中,添加此指令:?#pragma editor_sync_compilation
注意:對(duì)于在渲染過程中會(huì)遇到新變體的復(fù)雜著色器,不要強(qiáng)制進(jìn)行同步編譯。對(duì)復(fù)雜著色器進(jìn)行強(qiáng)制同步編譯可能會(huì)使 Editor 中的渲染停頓。
檢測(cè)編譯和刷新日期
如果虛擬著色器污染了生成的數(shù)據(jù),必須丟棄污染的數(shù)據(jù),并使用正確編譯的著色器來重新生成一組新數(shù)據(jù)。
如果已經(jīng)知道 Unity 用于生成數(shù)據(jù)的材質(zhì): 請(qǐng)使用?ShaderUtil.IsPassCompiled
?檢查由材質(zhì)當(dāng)前狀態(tài)指定的著色器變體編譯狀態(tài)。 該狀態(tài)從?Uncompiled?變?yōu)?Compiled?時(shí),請(qǐng)刷新所生成的數(shù)據(jù)。
如果不知道哪個(gè)具體材質(zhì)會(huì)生成污染的數(shù)據(jù),或者污染數(shù)據(jù)的生成并非與單個(gè)材質(zhì)相關(guān):
請(qǐng)使用?ShaderUtil.anythingCompiling
?檢測(cè) Unity 是否正在異步編譯任何著色器。 任何異步編譯完成后,請(qǐng)刷新數(shù)據(jù)。
在自定義 Editor 工具中使用異步著色器編譯
默認(rèn)情況下,異步著色器編譯功能在 Game 視圖和 Scene 視圖中有效。如果要在自定義 Editor 工具中使用該功能,可以通過 C# 為自定義工具啟用該功能。
在直接作用域中
在_直接作用域_中,必須使用?ShaderUtil.allowAsyncCompilation;
。
將?ShaderUtil.allowAsyncCompilation
?的當(dāng)前狀態(tài)保存到變量。 在調(diào)用要啟用異步編譯的渲染命令之前,立即將ShaderUtil.allowAsyncCompilation
?設(shè)置為?true
。 在希望影響的渲染調(diào)用之后,將?ShaderUtil.allowAsyncCompilation
?恢復(fù)到步驟 1 中保存的前一個(gè)狀態(tài)。
下面是一個(gè)偽代碼示例:
在 CommandBuffer 作用域中
在基于?CommandBuffer?的作用域中,必須使用?ShaderUtil.SetAsyncCompilation
?和?ShaderUtil.RestoreAsyncCompilation
。
在調(diào)用要啟用異步編譯的渲染命令之前,立即調(diào)用?ShaderUtil.SetAsyncCompilation
?并將其設(shè)置為?true
。緩沖區(qū)中所有緊隨其后的命令隨后會(huì)允許異步編譯。 在希望影響的渲染命令之后,使用?Shader.Util.RestoreAsyncCompilation
。
下面是一個(gè)偽代碼示例:
自定義編譯時(shí)間渲染
可以使自定義工具為每種材質(zhì)繪制非虛擬著色器的內(nèi)容。這樣就可以避免使用會(huì)變?yōu)榍嗌闹髯凅w來進(jìn)行渲染,而是在著色器編譯時(shí)繪制其他內(nèi)容。
要檢查是否已編譯特定著色器變體,請(qǐng)使用?ShaderUtil.IsPassCompiled
。
要手動(dòng)觸發(fā)編譯,請(qǐng)使用?ShaderUtil.CompilePass
。這樣就可以避免使用會(huì)變?yōu)榍嗌闹髯凅w來進(jìn)行渲染,而是在著色器編譯時(shí)繪制其他內(nèi)容。