(映維網 2020年01月03日)Oculus開發者關係團隊的一個主要任務是幫助開發者優化遊戲,令其能夠有效地支持所有的Oculus硬體。所述團隊中的開發者關係工程師克里斯蒂亞諾·費雷拉(Cristiano Ferreira)正在通過系列博文來介紹如何通過RenderDoc來優化你的Quest應用,包括如何輕鬆將其集成至你的工作流程,相關的基礎知識,以及允許你在未來利用這項有用工具的技巧經驗。對於第三篇博文,費雷拉探索了如何使以最有效的方式進行渲染,包括驗證CPU和GPU渲染方法是否能夠儘可能高效完成的關關鍵使用情景,下面是映維網的具體整理:
1. 驗證多通道 vs 實例立體渲染(instanced stereo rendering)
由於VR渲染的性質,確保繪製調用的最優化非常重要。在幕後,引擎分別渲染兩隻眼睛。每隻眼睛都有自己的模型視圖投影矩陣,而後者由透鏡設置限定的IAD(軸間距離)所定義。大多數人都知道IPD(瞳孔間距離)設置會影響軸間距離,商用引擎通常會為你處理這個問題,但你需要確保設置正確。下面是具體的概述:
1.1 單通道
對於基礎通道中的每個網格
繪製左眼(一次繪製調用)
繪製右眼(一次繪製調用)
1.2 多通道
對於基礎通道中的每個網格
繪製左眼(一次繪製調用)
對於基礎通道中的每個網格(再一次)
繪製右眼(一次繪製調用)
1.3 實例立體渲染/多視圖
對於基礎通道中的每個網格
繪製雙眼(一次繪製調用)
如你所見,實例立體渲染/多視圖是最佳選擇,因為它將使基本通道中的總繪製調用次數減少一半。
2. 驗證沒有使用臨時緩衝區
檢查輸出/渲染紋理,以確保資源名稱標記為RTTextureArray而非TempBuffer,ColorBuffer或其他名稱。要應用MSAA和/或節約固定注視點渲染量,你必須直接寫入交換鏈紋理。這同時將確保你不會意外地解析中間渲染目標/臨時緩衝區,從而節省1ms-1.5ms的固定GPU解析成本。
交換鏈紋理的正確名稱如下顯示:
這是寫入正確交換鏈紋理時的輸出紋理名稱。在這個示例中,你會看到選定的繪製調用位於名為「Final Blit Pass」的注釋之下。這意味著你要使用先前生成的渲染目標並將其用作臨時緩衝區,然後再把它複製到交換鏈紋理。GPU需要付出昂貴的解析成本,而僅將其映射到交換鏈紋理又要付出每像素成本。
我在前面顯示的同一捕獲幀中高亮了基礎通道繪製調用。相應輸出渲染目標顯示的標籤為「_CameraColorTexture」。這意味著引擎生成了一個中間渲染紋理並在後面用作blit/copy的輸入資源,這意味著解析成本,沒有固定注視點渲染像素著色器節省量和MSAA質量提升。這裡說明的是,你需要始終關注渲染目標,確保僅在單通道中直接渲染到交換鏈紋理。
在上面的螢幕截圖中,你可以看到Unity中的調試標記通過Resolve Shadows標記顯式注釋了解析陰影貼圖的過程。你同時可以看到Final Blit Pass批註,意味著將臨時顏色緩衝區複製到交換鏈紋理。
3. 驗證陰影投射器/接收器/級聯級別和陰影貼圖解析度(如若使用)
所有投射和/或接收陰影的網格都將需要從光源方向寫入深度緩衝區方向的額外繪製調用。儘管沒有針對這種繪製運行的片段著色器(這意味著它們的GPU開銷要低於典型的著色繪製),但CPU(繪製調用數增加)和GPU(頂點著色器投影所有網格幾何體+深度陰影貼圖解析)的開銷依然會產生。
上面是RenderDoc中的陰影貼圖。你會發現陰影通道中有一個用於稍後採樣場景陰影的臨時深度緩衝區。重要的是要驗證總體陰影貼圖解析度,以及與每個級聯相關的繪製調用次數。在幾乎所有的情況下,隨著camera距離的增加和每增加一個級聯,較接近的級聯將具有較少的繪圖調用。請注意橙色的解析陰影一欄。當你將臨時緩衝區存儲在外部存儲中時,這是固定成本。如果必須使用陰影,請確保將解析度和其他繪製調用保持在儘可能低的水平。
4. 驗證共享網格的GPU實例化
GPU實例化是圖形API/驅動程序級別的功能,可允許你調度單個繪製調用來渲染多個網格。這裡需要遵守一定的規則,包括共享完全相同的管道狀態和網格輸入。如果一個網格每幀使用了多次(子彈,裝飾,粒子和樹葉等),則可以通過實例化網格來大幅減少繪製調用。請注意,這不會降低GPU的成本,因為你依然必須投影每個網格。
每當你調度一個實例化繪製,每個網都會收到可以在著色器中使用的實例ID。這樣,你就可以將每個網格實例的實例ID與屬性信息(如紋理索引,顏色或其他)相關聯。我一直十分喜歡舉的一個例子是,在賽車遊戲中渲染大量人群。你可以通過實例化並使用不同顏色的襯衫來繪製每個人,從而確保一種觀眾滿滿的感覺,同時不必為每個網格分別調度單獨的繪製調用而產生過多的CPU開銷。在這個基本示例中,你將不會使用蒙皮網格……但你可以在GPU進行蒙皮/動畫操作來實例化蒙皮網格。這是一種更先進的技術,但同樣有著優點和缺點。這裡的想法是,你會實例繪製共享的T形網格,然後使用實例ID查找並計算時間戳和姿態信息。這樣可以完成混合形狀查找,並在頂點著色器中進行蒙皮。或者,你可以提前在計算著色器中批量處理所有混合形狀和蒙皮操作,然後使用實例ID查找當前繪製的網格的相關數據的位置。
旁註:GPU實例化是實例立體/多視圖渲染的工作方式。使用所述方法的每個繪製調用為每個網格調度兩個實例。兩個網格映射中的每一個實例ID均繪製到相關的眼睛緩衝區,而頂點著色器將用來索引到模型視圖投影矩陣的一個陣列,從而正確地將網格投影到相關的眼睛位置。
在上圖中,你看到的不是十二個單獨的glDrawElements(36)調用,而是4x glDrawInstanced。glDrawElementsInstanced(36,5)不能一次繪製所有多維數據集的原因是,某些多維數據集違反了Unity中的批處理規則。要判斷未對它們進行批處理的原因,你可以通過Unity中的Frame Debugger工具來確定是否調用了glDrawElementsInstanced(36,12),從而將多維數據集的總繪製計數縮減為一個繪製調用。
在上面的Frame Debugger視圖中,我選擇了第二個實例繪製調用。Unity在「Why this draw call can』t be batched with the previous one」一欄中表示,多維數據集受不同的反射探針所影響。如果它們全部在一次繪製調用中完成,我將需要對所有多維數據集使用單個反射探針。Frame Debugger免除了方程式中的猜測,而你同時可以通過RenderDoc的「Pipeline State」選項卡中使用傳統的偵探方法來得出這個結論。只需檢查並比較兩個實例繪製調用之間的輸入資源即可。
5. 渲染隊列和z-sorting驗證
對於大多數GPU,有一種優化方法可允許硬體拒絕遮擋的不透明像素(從前到後繪製)。例如在VR中,在大多數情況下如果手不透明,則應首先渲染它們。如果在繪製手(可能是接近於camera的對象之一)時寫入深度緩衝區,並調度z-test以拒絕其後的像素,則硬體將足夠智能並選擇不為位於手後面的投影幾何像素執行像素著色器。如果你使用了合理的幾何圖形計數,並且頂點著色器中沒有執行任何花哨的操作,則像素著色器通常是渲染管道中開銷最多的一環。
需要注意的重要一點是,由於Rift S和Quest採用了高解析度面板,並且你必須為雙眼繪製,所以這裡節省量的價值二將會翻倍。在特定情況下,使用成本昂貴的像素著色器來為植物提供一個Depth Prepass物有所值,亦即為Alpha!= 0像素設置深度緩衝區,然後再執行一次彩色通道,將深度測試設置為相等。使用這兩種方法進行實驗將幫助你得到正確的結果。
下面是一個示例:
上圖是一個問題場景中的最終渲染目標。一切看起來都不錯,對吧?也許建築物內部存在對象渲染,它們不僅使用了不必要的繪製調用並在CPU端施壓管道狀態,並且正通過投影所有內部對象的幾何形狀及遮蓋所有不必要的投影來占用GPU資源。請看看下面的螢幕抓圖,它說明了這種低效情況:
帶注釋的繪製只是分配給GPU的所有不必要繪製調用的一小部分。它們無緣無故地對所有幾何圖形進行處理和著色。如果CPU採取預防措施來遮擋剔除它們,則GPU將不必擔心它們。我們會在後面介紹更多關於遮擋提出的細節。
對於這種情況,一個合適的比例是:你聘請了一位油漆工,然後他以每小時的速度來油刷房屋。如果你決定先將所有框梁都塗成棕色,然後用純色塗漆整棟房屋。這時候,如果你已經告訴他們要用一種顏色塗漆房屋,但工人卻把框梁都塗成紅色,而且你依然要為其付費,相信你會感到非常不解和沮喪。這可是成本。在遊戲和圖形世界中,GPU正在處理你本可以花費在其他地方的開銷,如使用更詳盡的著色器/材質。為了進行比較,請參閱下圖。這裡顯示的是同一幀,但選擇了在調度順序中更後的繪製調用。
如前所述,當你在RenderDoc的API日誌中選擇繪製調用時,幀會按時間順序在你眼前構建。在早前的截圖中,當我在數個調用之後選擇了這個繪製時,你會看到一堆牆壁模型遮擋了先前繪製的所有內容。這意味著你先前調度的所有繪製調用均執行了其像素著色器並被覆蓋。如果你在那些對象之前繪製了環境牆壁和天花板,則應該將深度緩衝區設置為牆壁網格的深度,從而節省每個像素片段著色器的過度繪製成本,因為硬體可以判斷較近的像素正在遮擋它。如果你的遊戲負擔不起遮擋剔除,你依然可以通過為遊戲量身定製優化的排序算法來節省GPU時間。Unity和Unreal均允許覆蓋渲染隊列中的特定圖形和材質。
6. 紋理格式和解析度驗證
利用RenderDoc,你可以驗證輸入和輸出的紋理解析度是否太高,是否提供了mipmap,壓縮格式是否符合你的期望,每次繪製採樣的紋理數量等等。你在引擎編輯器中很難跟蹤所有紋理的紋理信息,所以這是一個節省內存並保持效率的優秀驗證方法。
為了感受具體的差異,下面我們來看看一個輸入紋理:
例如,當我在「Input」選項卡中選擇了這個Ogre繪製調用,然後選擇albedo輸入紋理時,我可以看到每個輸入紋理的解析度,壓縮格式和mip級別數量。我可以看到ASTC texture compression格式用於我的輸入紋理。這是獲得最佳質量和大小的推薦格式。
大多數情況下,高動態範圍(HDR)紋理格式對Oculus Quest而言都不可行。HDR需要一個臨時緩衝區,其格式與交換鏈紋理的格式不同,其通常是R11G11B10_FLOAT,而不是普通的R8G8B8A8_SRGB。問題不是bits per-pixel,而是當每幀準備就緒時將臨時緩衝區從HDR複製和轉換為標準格式的blit成本。由於需要更高的浮點精度,所以HDR效果和計算的成本通常要會昂貴得多。同樣,任何時候使用臨時緩衝區都不會獲得固定注視點渲染或MSAA的好處,所以你可能會遭受額外的性能和/或質量損失。要確定渲染目標是否符合HDR規範,你可以確保自己正在寫入採用R8G8B8A8_SRGB格式的RTDeviceEyeTextureArray交換鏈紋理來,如下面的比較圖所示:
7. 總結
本文介紹了從Oculus Quest中實現最優性能的重要案例。請留意下一篇RenderDoc博文,我將介紹更多的使用場景及如何利用這個強大工具的建議。
延伸閱讀:研發實戰:如何通過RenderDoc優化Quest應用
延伸閱讀:研發實戰:用RenderDoc+固定注視點等渲染技術優化Quest應用
原文連結:https://yivian.com/news/70527.html