這篇文章根據我們目前在游戲中使用 NVIDIA RTX 光線跟蹤的經驗收集了最佳實踐。實用技巧被組織成簡短的、可操作的項目,供今天從事光線跟蹤工作的開發人員使用。他們旨在深入了解什么樣的解決方案在大多數情況下會帶來良好的性能。為了找到特定案例的最佳解決方案,我總是建議進行分析和實驗。
本文中使用的常見縮寫和短詞:
AABB: 軸對齊邊界框
AS :加速度結構
BLAS: 底層加速結構
Geometry: 和 BLAS 中的幾何體
Instance: TLAS 中 BLAS 的實例
TLAS: 頂層加速結構
加速結構
本節重點介紹光線跟蹤加速結構的構建和管理,這是將光線跟蹤用于任何目的的起點。主題包括:
一般提示
建筑時最大化 GPU 利用率
內存分配
將幾何圖形組織為 BLAS
生成首選項標志
動態 BLASE
非不透明幾何形狀
粒子
一般提示
考慮異步計算作為構建。 特別是在混合渲染中, G 緩沖區或陰影貼圖被光柵化,在異步計算的基礎上執行可能是有益的。
考慮將工作線程生成為構建命令列表。 生成生成生成命令可能包括大量 CPU 端工作。它可以直接在編譯調用中,也可以直接在一些相關任務中,如對象的剔除。將 CPU 工作轉移到一個或多個工作線程可能是有益的。
TLA 的剔除實例。 通常,在 TLAS 中包括整個場景不是最佳的。相反,根據情況挑選實例。例如,考慮基于擴展的相機截錐體進行消隱。在光柵化中,最大距離通常可以小于遠平面距離。在剔除時,還可以考慮實例大小,以便在較短的距離內剔除較小的實例。
對實例使用適當的詳細程度( LOD )。 與光柵化一樣,對所有內容使用最詳細的幾何 LOD 通常是次優的。用于遠距離對象的 LOD 可以更簡單。在混合渲染中,可以考慮使用相同的 LOD 進行光柵化和光線跟蹤。這是避免自相交偽影(如曲面陰影本身)的有效方法。
還應考慮在光線跟蹤中使用較低細節 LOD ,特別是為了降低動態 BLAS 的更新成本。如果光柵化和光線跟蹤之間的 LOD 不匹配,則在光線跟蹤中通常需要啟用背面消隱,以防止自相交。有關光線跟蹤中 LOD 的更多信息,以及如何實現隨機 LOD ,請參見 使用 Microsoft DirectX 光線跟蹤實現隨機細節級別 。
盡可能將幾何體或實例標記為不透明。 將實例或幾何體標記為不透明允許不間斷的硬件交叉點搜索,并防止調用任何命中著色器。盡可能做到這一點。只允許對需要的幾何體使用任何命中著色器;例如,進行 alpha 測試。
盡可能使用三角形幾何形狀。 硬件擅長執行光線三角形相交。光線盒交點也會加速,但在跟蹤三角形幾何體時,您可以充分利用硬件。
建筑時最大化 GPU 利用率
批處理頂點變形和 BLAS 構建。 連續執行所有頂點變形調用,生成用作 BLAS 構建和所有 BLAS 構建調用輸入的三角形。不要在連續通話之間設置資源障礙。這允許驅動程序在一定程度上并行調用。所有 BLAS 構建調用都需要唯一的暫存內存,以允許無障礙執行。
無需為每個資源持有區設置單獨的無人機屏障。相反,在 TLAS 構建之前,您可以有一個單一的全球 UAV 屏障,以確保所有 BLAS 構建都已完成,無論它們位于何處。
考慮合并小頂點變形調用。 通常,為一個幾何體或實例輸出變形頂點的調用是輕量級的,即使在連續調用之間沒有障礙的情況下執行,也不會填充整個 GPU 。將多個幾何圖形或實例的處理合并到一個調用中可以提高 GPU 利用率并產生更好的性能。
內存分配
匯集小額撥款: BLASE 可以很小,有時只有幾千字節。使用單獨的提交資源來存儲每個這樣的小 BLA 并不是最優的。相反,用更大的資源集中他們。池可以節省內存,通常可以提高性能。一種選擇是在大型資源堆中使用放置的資源。
或者,通過手動從緩沖區中分配部分,可以將許多 BLAE 存儲在單個緩沖區中。由于子分配只需遵循 256 字節對齊,因此這允許更緊密地將 BLASE 備份到內存中。無論采用何種池機制,都要避免內存碎片,以保持池帶來的好處。
考慮壓縮靜態 BLAS: 壓縮 BLASE 可以節省內存并提高性能。內存消耗的減少取決于幾何形狀,但可能高達 50% 左右。由于在 GPU 上完成 BLAS 構建后,需要將壓縮大小讀回 CPU ,這對于只構建一次的 BLAS 最為實用。請記住,要集中小的分配并避免內存碎片,以從壓縮中獲得最大的好處。
將幾何圖形組織為 BLAS
當實例的世界空間 AABB 中有很多空白空間時,考慮拆分 BLA 。 世界空間 AABB 用于測試光線是否可能擊中實例并遍歷其相關 BLA 。大量的空白會導致通過 BLAS 進行不必要的遍歷。
獨立移動的幾何體通常應該在自己的 BLAS 中。將它們合并到單個 BLAS 中可能會導致一個具有大量空白空間的 AABB ,以及不必要的 BLAS 重建,而不是簡單地更改獨立實例的轉換。
當實例世界空間 AABB 顯著重疊時,考慮合并 BLASE 。 當實例的世界空間 AABBs 重疊時,穿過該區域的每條光線必須分別處理所有重疊的 BLAS 實例,以找到潛在的交點。遍歷一個合并的 BLA 將更有效。
針對 BLAS 的跟蹤性能不取決于其中幾何體的數量。合并到單個 BLAS 中的幾何體仍然可以具有獨特的材質。
盡可能實例化 BLAS 。 實例化 BLASE 可以節省內存。它還可以提高光線跟蹤性能。實例可以具有唯一的材質和變換。在實例的 AABB 重疊很多的情況下,盡管內存消耗增加,但將它們復制并合并為單個 BLA 作為多個幾何體仍然是更好的選擇。
避免幾何形狀中的細長三角形。 長而薄的三角形具有非最佳邊界體積,具有大量的空白空間。它們很容易與許多其他邊界體積重疊。當根據幾何體跟蹤光線時,這會導致非最佳性能。
駕駛員可以根據幾何形狀在一定程度上緩解問題。第一個三角形不太可能引起問題,但太多的三角形確實會引起問題,因此我建議盡可能避免它們;例如,將它們分割成較小的三角形。
不要在 TLA 中包含天空幾何體。 天盒或天球將有一個 AABB ,與其他所有物體重疊,所有光線都必須進行測試。對于表示天空的幾何體,在未命中著色器中處理天空著色比在命中著色器中更有效。
生成首選項標志
對于 TLA ,考慮PREFER_FAST_TRACE標志并僅執行重建。通常,這會產生最佳的整體性能。其基本原理是,無論場景中發生何種移動,盡可能高質量地制作 TLA 都很重要,而且成本不高。
對于靜態 BLASE ,使用PREFER_FAST_TRACE標志。對于所有只構建一次的 BLASE ,優化最佳光線跟蹤性能是一個簡單的選擇。
對于動態 BLASE ,請選擇使用PREFER_FAST_TRACE或PREFER_FAST_BUILD標志,或者兩者都不使用。對于偶爾重建或更新的 BLAE ,最佳構建首選項標志取決于許多因素。建造了多少?射線痕跡有多貴?可以通過在異步計算上執行構建來隱藏構建成本嗎?為了找到特定情況下的最佳解決方案,我建議嘗試不同的選項。
動態 BLASE
盡可能重復使用舊 BLA 。 如果您知道 BLAS 的頂點在上次更新后沒有移動,請繼續使用舊 BLAS 。
僅為可見對象更新 BLAS 。 當從 TLA 中剔除實例時,也將其剔除的 BLAS 從 BLAS 更新過程中排除。
考慮根據距離和大小跳過更新。 有時不需要在每一幀上更新 BLA ,這取決于它在屏幕上的大小。可以跳過某些更新,而不會引起明顯的視覺錯誤。
在大變形后重建 BLASE 。 有限變形后的 BLAS 更新是一個不錯的選擇,因為它們比重建便宜得多。然而,先前重建后的大變形可能會導致非最佳光線跟蹤性能。細長的三角形加劇了這個問題。
考慮定期重建更新的 BLASE 。 當幾何體變形過大并且需要重建以恢復最佳光線跟蹤性能時,可以進行檢測。簡單地定期重建所有 BLASE 是一種合理的方法,可以避免顯著的性能影響,而不考慮變形。
在框架上分布重建。 由于重建比更新慢得多,因此在單個幀上進行的許多重建可能會導致口吃。為了避免這種情況,最好將重建分布在框架上。
考慮僅使用具有不可預測變形的重建。 在某些情況下,當幾何體變形足夠大和快速時,在構建 BLAS 時省略ALLOW_UPDATE標志并始終重建它是有益的。如果需要,可以考慮使用PREFER_FAST_BUILD標志來降低重建成本。在極端情況下,使用PREFER_FAST_BUILD標志會比使用PREFER_FAST_TRACE標志和更新產生更好的整體光線跟蹤性能。
避免在 BLAS 更新中更改三角形拓撲。 更新中的拓撲變化意味著三角形退化或復活。如果退化三角形的位置不代表恢復三角形的位置,則可能導致非最佳光線跟蹤性能。“彎曲”變形中偶爾發生的拓撲變化通常不會造成問題,但“斷裂”變形中較大的拓撲變化可能會造成問題。
在可能的情況下,最好使用單獨的 BLAS 版本,或對“破壞”變形引起的不同拓撲使用非活動三角形。當三角形的位置為 N A N 時,三角形處于非活動狀態。如果這些替代方案不可行,我建議重建 BLAS ,而不是在拓撲更改后更新。更新中不允許通過索引緩沖區修改來更改拓撲。
非不透明幾何形狀
盡可能減少非不透明區域。 調用任何命中著色器(通常用于對非不透明三角形執行 alpha 測試)會中斷硬件交點搜索。盡可能減少未標記為不透明的區域是提高性能的簡單方法。使用更多的三角形更準確地定義非不透明區域可能是一個很好的權衡。
考慮拆分為不透明和非不透明幾何體。 當定義良好的幾何三角形部分可以被視為完全不透明時,可以考慮將其拆分為單獨的幾何體并將其標記為不透明。不同的幾何形狀仍然可以駐留在同一 BLAS 中。
粒子
考慮將公告牌粒子表示為三角形幾何體。 在 BLASE 中表示廣告牌粒子的一個選項是將廣告牌輸出為三角形,將廣告牌的一部分沿垂直軸旋轉 90 度到不同的方向。這允許利用三角形相交硬件,同時為粒子的視覺邊界提供合理的近似。
考慮 alpha 測試而不是混合。 根據粒子類型,在二次光線中對渲染主可見性時混合的粒子進行 alpha 測試可能會提供合理的視覺質量。這種方法最適用于邊界清晰的粒子。對于表示煙或霧等物體的粒子,這可能不適用。有關更多信息,請參閱 光線跟蹤 《沃爾芬斯坦:年輕的血液》中的思考 。
避免對死粒子使用退化三角形。 更新 BLASE 中的退化三角形可能會使結構對于光線跟蹤而言不是最優的。對于具有動態活粒子數的粒子系統,我建議考慮其他解決方案,例如使用正確的粒子數在每個幀上重建 BLAS 。
考慮將網格粒子表示為 TLA 中的實例。 對于渲染為三角形網格的粒子,每個粒子都有一個唯一的實例是一個合理的解決方案。當粒子在場景周圍分布時,這是真實的,因此單個光線通常不會擊中許多實例。實例應共享基礎網格 BLAS 。此外,考慮壓縮 BLAS 。
點擊著色
本節重點介紹光線命中的著色。即使是經驗豐富的圖形開發人員在開始開發光線跟蹤著色器時也可能會受益于新想法,因為最佳解決方案可能與光柵化中的不同。主題包括:
一般提示
最小化分歧
任何命中著色器
著色器資源綁定
內聯光線跟蹤
管道狀態
一般提示
保持光線有效載荷較小。 寄存器用于保存有效負載值,它們減少了命中著色器可用的寄存器數量。我建議避免草率使用有效負載,盡管向包值中添加復雜代碼很少有好處。
使用有效負載訪問限定符。 此功能在 HLSL 著色器模型 6.6 中可用。它允許指定哪些著色器階段寫入或讀取有效負載中的每個字段,并使編譯器能夠更好地優化寄存器使用,從而提高占用率和性能。為了獲得最大的潛在效益,請盡可能準確地定義每個字段的限定符。有關更多信息,請參閱 GitHub 上的 DirectX-Specs 。
考慮將安全默認值寫入未使用的有效負載字段。 當某些著色器不使用負載中其他著色器所需的所有字段時,仍然可以將安全默認值寫入未使用的字段。這允許編譯器在寫入之前丟棄未使用的輸入值,并將有效負載寄存器用于其他目的。
盡可能在第一次擊中時終止射線。 當不需要解析正確的最近命中時(對于陰影光線),使用RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH或gl_RayFlagsTerminateOnFirstHitEXT標記光線是一種簡單有效的優化。
僅當需要正確性時才使用面剔除。 與光柵化不同,啟用背面或正面消隱不會提高性能。相反,它稍微減慢了光線遍歷。僅當需要獲得正確的渲染結果時才使用它們。
最小化光線跟蹤調用的活動狀態。 在TraceRay或traceRayExt調用之前初始化并在調用后使用的變量是活動狀態,在調用命中和未命中著色器時必須在調用過程中保持這些狀態。司機有幾種不同的選擇,但都有成本。
我建議盡量減少活動狀態的數量。識別這樣的變量并不總是微不足道的。 NVIDIA 和微軟正在合作開發一種編譯器功能,用于自動檢測活動狀態。
避免深度遞歸。 深度、非均勻光線遞歸可能代價高昂。
最小化分歧
對每個材質模型使用單獨的命中著色器。 減少命中著色器中的代碼和數據發散是有幫助的,尤其是在非相干光線的情況下。特別是,避免在材質模型之間手動切換的 U bershader 。在單獨的命中著色器中實現每個所需的材質模型,為系統提供了管理發散命中著色的最佳可能性。
當材質模型允許使用統一的著色器而沒有太多分歧時,可以考慮對具有各種材質的幾何體使用公共命中著色器。
考慮簡化著色。 通常,不需要復制用于渲染主要可見性的所有功能,以進行著色鏡面反射或間接漫反射照明。忽略特征并不總是導致顯著的視覺差異。或者,視覺效果的改善并不能證明渲染成本是合理的。光線越不相干,通常需要的主要可見性特征的復制越不準確。此外,隨著命中距離的增加,著色有時可以進一步簡化。
避免直接從頂點和像素著色器轉換。 在命中著色中獲得最佳性能的方法不同于光柵化的最佳方法。在光柵化中,即使代碼差異很小,也可以使用單獨的著色器置換。在命中著色中,減少單個命中著色器內的發散度和單獨命中著色器的數量都很有幫助。通常,我不建議直接將頂點和像素著色器轉換為命中著色器。
考慮將公共代碼移到命中和未命中著色器之外。 當所有命中著色器都有一個公共部分時,我建議將該代碼從命中著色器中移除;例如,到光線生成著色器。有時,命中著色器和未命中著色器中也可能存在常見代碼,例如,當命中著色器中下一次反彈的近似值與未命中著色器中第一次反彈的近似值相同時。同樣,我建議將該常見代碼移到命中和未命中著色器之外。
任何命中著色器
更喜歡統一和簡化的任何命中著色器。 在光線遍歷期間,可能會大量執行任意命中著色器,并且它會中斷硬件交點搜索。任何命中著色器的成本都會對整體性能產生顯著影響。我建議在光線跟蹤過程中使用統一且簡化的任意命中著色器。此外, GPU 的完整寄存器容量不適用于任何命中著色器,因為它的一部分被驅動程序用于存儲光線狀態。
優化對材料數據的訪問。 在任何命中著色器中,對材質數據的最佳訪問通常至關重要。一系列相關內存訪問是一種常見模式。加載頂點索引、頂點數據和采樣紋理。在可能的情況下,從該路徑中刪除間接操作是有益的。
混合時,請記住未定義的點擊順序。 沿光線的點擊被發現,并且相應的任何點擊著色器調用以未定義的順序發生。這意味著混合技術必須與順序無關。這還意味著,為了排除最近的不透明命中之外的命中,必須適當限制光線距離。此外,可能需要使用NO_DUPLICATE_ANYHIT_INVOCATION標記混合幾何體,以確保結果正確。有關更多信息,請參閱 光線跟蹤寶石 中的第 9 章。
著色器資源綁定
如果可能,首選全局根表( DXR )或直接描述符訪問( Vulkan )。 通常,光線生成和未命中著色器使用的資源可以像計算著色器一樣方便地綁定,而不是通過著色器記錄綁定。此外,不管命中了什么,通常也可以這樣綁定使用的命中著色器資源。在所有命中記錄中具有相同的資源限制不是最優的。
考慮 hit 著色器的無綁定資源。 無界描述符表( DXR )或無大小描述符數組( Vulkan )中的資源,由命中特定的系統值(如 InstanceIndex 或 gl _ InstanceID )或直接存儲在命中記錄中的值( DXR 中的根常數)索引,可以是向命中著色器提供資源的有效方法。
考慮索引和頂點緩沖區的根描述符。 ( DXR )作為無界描述符表的替代方法,可以高效地將索引和頂點緩沖區地址直接存儲在命中記錄中作為根描述符。當通過根描述符訪問資源時,不會隱式執行越界檢查。根描述符地址必須遵循四字節對齊。預計算到基址的 16 位索引的偏移量可能會破壞對齊。
盡可能使用根簽名版本 1.1 和靜態描述符。 ( DXR )根簽名 1.1 允許驅動程序預期描述符是靜態的;也就是說,在記錄命令列表后,應用程序不會修改它們。這可以在驅動程序中實現一些潛在的有益優化,尤其是當根描述符不用于訪問緩沖區時。與根描述符一樣,越界檢查不是用靜態描述符隱式執行的。此外,靜態描述符和根描述符都不能為 null 。
考慮在 GPU 上構建著色器表。 當有許多幾何體和許多光線跟蹤過程時,命中表可能會變大,上載它們可能會耗費大量時間。與其上載在 CPU 上構建的整個命中表,不如只上載每個幀上所需的新信息,例如當前可見實例的材質索引,然后在 GPU 上執行命中表構建過程以提高效率。
表構造中所需的大部分信息可以永久駐留在 GPU 內存中,例如命中組標識符、頂點緩沖區地址和幾何體的偏移量。
內聯光線跟蹤
考慮螺紋組尺寸為 8 × 8 或更大。 作為計算著色器進行內聯光線跟蹤的經驗法則,可以使用大小為 8 × 8 的線程組。通常,一組中的線程數是 GPU 波大小的倍數是有效的。 NVIDIA GPU 中的波形大小為 32 個線程。
然而,由于同時執行的組數量有限,使用只有一個波形的線程組限制了線程占用。一組中有兩個波浪會使潛在占用率翻倍。著色器寄存器和組共享內存消耗也可以設置占用限制。當其他因素允許時,可以從三個波組開始達到最大線程占用率。
組大小的實際選擇可以是 16 × 8 螺紋。將尺寸增加到遠遠超出此范圍通常是沒有好處的。通過不同尺寸的實驗,可以發現針對特定情況的最佳尺寸。不同硬件代的最佳尺寸可能不同。
使用內聯光線跟蹤避免發散著色。 由于未基于命中調用命中著色器,因此所有著色都在投射光線的著色器中內聯發生。在根據點擊數選擇的著色器中具有發散的代碼路徑或數據訪問可能會減慢著色速度,尤其是在光線不相干的情況下。當需要多個不同的著色模型時,使用DispatchRays或vkCmdTraceRaysKHR是更好的選擇。
使用 hit 特定的系統值進行內聯光線跟蹤的無綁定資源訪問。由于命中記錄中的綁定不可用,因此必須通過其他方式提供特定于幾何體的綁定。基于特定于 hit 的系統值(如InstanceContributionToHitGroupIndex和GeometryIndex)訪問無界描述符表中的資源是一種很好的做法。
我建議盡可能避免間接訪問索引、頂點和材質數據。例如,基于系統值(如InstanceID)從緩沖區讀取資源索引以選擇索引緩沖區可能會導致難以隱藏的延遲。
首選編譯時光線標志。 編譯時和運行時光線標志都可以用于內聯光線跟蹤。我建議盡可能使用編譯時標志,因為它們可以實現有益的編譯時優化。
監視查詢對象的寄存器消耗。 初始化后,當著色器執行可能繼續遍歷的代碼時,查詢對象必須保持光線遍歷的狀態。這會消耗寄存器,復雜的用戶代碼可能會比通常更快地限制占用。這種情況類似于在DispatchRays或vkCmdTraceRaysKHR過程中執行任何命中著色器。在使用查詢對象之前初始化并在之后使用的變量可能會消耗額外的寄存器。
考慮線程組重新排序以提高一致性。 當使用來自計算著色器的內聯光線跟蹤時,調度線程組的默認行主分配到 GPU 執行通常不會產生最佳性能。通過手動重新排序線程組,可以提高在 GPU 上執行時線程組同時進行的內存訪問的一致性。有關更多信息,請參閱 光線跟蹤管道狀態的并行著色器編譯 。
管道狀態
考慮每個光線生成著色器一個狀態對象。 我建議為每個DispatchRays或vkCmdTraceRaysKHR調用使用該過程中所需的著色器編譯一個單獨的狀態對象。它可以幫助優化寄存器消耗,并允許優化本文后面描述的管道配置值設置。
將MaxTraceRecursionDepth、MaxRecursionDepth、MaxPayloadSizeInBytes , 和MaxAttributeSizeInBytes設置得盡可能小。將這些值設置為高于必要值可能會對性能產生不必要的負面影響。在DispatchRays或vkCmdTraceRaysKHR調用中使用內聯光線跟蹤時,這些光線跟蹤調用不計入最大遞歸深度。
盡可能使用SKIP_PROCEDURAL_PRIMITIVES, SKIP_AABBS , 和SKIP_TRIANGLES。這些管道狀態標志允許在狀態編譯中進行簡單但潛在有效的優化。
考慮使用著色器集合進行并行編譯和共享。 ( DXR )當您管理多個著色器時,著色器集合可能允許多線程編譯狀態對象,并在狀態對象之間共享編譯代碼。有關更多信息,請參閱 光線跟蹤管道狀態的并行著色器編譯 。
當需要自動綁定點分配時,請考慮編譯器選項。 ( DXR )默認情況下,編譯著色器庫時不使用著色器資源的自動綁定點指定。如果需要,有幾個有用的編譯器選項。首先,/auto-binding-space在給定寄存器空間中啟用自動綁定點分配。此外,默認情況下,所有未標記關鍵字 static 的函數都被視為庫導出。
使用/auto-binding-space時,任何導出函數訪問的資源都會消耗綁定點,而不管它們是否在最終狀態對象中使用。為了將綁定點消耗限制為真正需要的函數,可以使用/exports來限制庫導出。
考慮AddToStateObject的增量構建。它允許基于現有對象增量構建狀態對象,這在使用多個著色器管理動態內容時非常有用。
M 如果適用,每年管理堆棧。 使用 API 的查詢函數來確定每個著色器所需的堆棧大小,并應用有關調用圖的應用程序端知識來減少內存消耗并提高性能。
一個很好的例子是拍攝二次陰影光線的昂貴反射著色器,應用程序知道,二次陰影光線僅使用具有低堆棧要求的普通命中著色器。驅動程序無法提前知道此調用圖,因此默認的保守堆棧大小計算會過度分配內存。
關于作者
Juha 是一名軟件工程師,在實時圖形方面有 15 年的經驗。他曾在各種游戲、引擎和硬件基準測試中使用過尖端渲染技術。最近,他專注于 RTX 光線追蹤在游戲中的應用。
審核編輯:郭婷
-
寄存器
+關注
關注
31文章
5317瀏覽量
120006 -
NVIDIA
+關注
關注
14文章
4936瀏覽量
102814
發布評論請先 登錄
相關推薦
評論