內容清單
簡介
「增強型屏障」(EB) 是一組新的 D3D12 API,旨在解決舊版資源屏障 API 所帶來的一些問題 (例如資源狀態提升與衰減等令人困惑的概念、別名屏障的設計問題、過多的排清作業等),同時讓開發人員能夠獨立控制 GPU 工作同步處理。相較於舊版資源屏障,它們提供了表達能力更強的屏障類型。 增強型屏障的功能包括:
- 減少同步延遲與過多的快取排清。
- 不再有難以理解的提升與衰減規則。
- 快速、彈性的資源別名。
- 在屏障轉換期間捨棄。
- 同時讀取/寫入以及自我複製。
XBOX Series X|S 上的 GPU 同步處理
在繼續了解增強型屏障之前,需要先介紹 XBOX Series X|S 上的同步處理。務必了解該節中說明的概念與機制,因為它們是屏障的基礎。 XBOX Series X/S 上的 GPU 同步處理 現在我們可以深入探討增強型屏障,並更詳細地說明其運作方式。
深入了解屏障
增強型屏障在決定如何轉換任何指定資源 (或記憶體) 時,引進了三個實用的概念。分別是 Sync、Access 和 Layout。在某種程度上,它們取代了資源屏障先前使用的資源狀態概念。不過它們並不相同,因為資源狀態一律可以表示為 Sync、Access 和 Layout 的組合,但 Sync、Access 和 Layout 的組合並不一定能表示為資源狀態。新的 API 表達能力更強。 Sync (之前-之後) 用於讓驅動程式知道我們需要等待哪個管線階段完成,以及在完成屏障作業之前應封鎖哪個引擎的執行。
- 下表顯示部分 sync 旗標如何轉譯為需要排空或封鎖之階段的需求 (請參閱管線化一節)。值得注意的是,部分屏障 sync 旗標指的是廣泛的階段 (例如複製),但在內部,會根據 access 和 layout 提供的資料對應至特定階段 (複製來源/複製目的地),進而產生更適當的排空/封鎖需求。此外,下表將以紋理的情況為例 (對於緩衝區,部分階段會有所不同)。
- 這些需求稍後會被剖析,並與其他基本類型一起傳遞給 SyncBottomOfPipe/SyncTopOfPipe 函式,以決定最終的同步處理動作 (例如 D3D12XBOX_SYNC_FLAG_PS_PARTIAL_FLUSH 或 D3D12XBOX_SYNC_FLAG_IDLE)。請勿將這些旗標與 D3D12_BARRIER_SYNC 旗標混淆,後者用於增強型屏障呼叫。
- 這些需求並不知道最終用來滿足它們的機制,因此會明確指出哪個硬體元件需要排空/封鎖。例如,後續元件可能會發現多個排空可以透過一個 EoP 事件完成。
- 在屏障的情境下,了解哪些資源對應至哪些快取很有用。例如,如果 AccessBefore 為 D3D12_ACCESS_RENDER_TARGET,而之後的 access 為 D3D12_ACCESS_SHADER_RESOURCE,我們會想知道要使哪些快取失效和/或排清 (如有必要)。下表顯示一些常用的資源類型及其使用的記憶體。
- 資源類型並不是決定資源使用哪些快取的唯一因素。作業類型 (純量與向量) 也很重要,而這是由編譯器決定的。例如,從常數緩衝區擷取時,如果是純量作業 (SMEM),可以透過 $K-L2 路徑;如果是向量作業 (VMEM),則透過 L0-L1-L2。
Layout (之前-之後) 描述資源的記憶體配置。它會轉譯為資源在屏障作業中是否需要解壓縮/壓縮,以及是否需要初始化中繼資料 (適用於 rendertarget 和 depthstencil)。例如,啟用 DCC 的 rendertarget 在轉換為 SRV 時可能需要執行 DCC-Eliminate,因而進行解壓縮。
- 某些資源和屏障旗標會改變壓縮/解壓縮的結果需求:
- 驅動程式會保留兩個表格,將資源類型對應至壓縮/解壓縮作業。其中一個表格適用於以 D3D12XBOX_RESOURCE_FLAG_FORCE_TEXTURE_COMPATIBILITY 建立的資源,另一個則適用於非 TC 資源。
- EB 引進了 D3D12_TEXTURE_BARRIER_FLAG_DISCARD,當其作為引數傳遞給 Barrier 方法時,會強制初始化中繼資料。在 EB 之前,這可以透過在首次使用資源之前執行清除作業來完成。
- Rendertarget 和 depthstencil 必須分別以 D3D12_RESOURCE_FLAGS_ALLOW_RENDER_TARGET 和 D3D12_RESOURCE_FLAGS_ALLOW_DEPTH_STENCIL 建立,才能正確配置中繼資料表面。
- 如果您想使用 DCC,資源必須以 D3D12XBOX_RESOURCE_FLAG_ALLOW_DCC 旗標建立。DCC 意味著從寫入狀態轉換為讀取狀態時會進行解壓縮作業,但如果建立資源時也使用了 D3D12XBOX_RESOURCE_FLAG_FORCE_TEXTURE_COMPATIBILITY,則不會。
- 下表將 layout 旗標與壓縮狀態旗標對應,有助於更清楚了解轉換進出如何造成壓縮/解壓縮:例如,在非 TC 資源上從 rendertarget (rt) 轉換為 shader resource (sr) 需要解壓縮,因為 rt 具有數個壓縮旗標,而 sr 標記為 uncompressedAll。但對於 TC 資源,相同的轉換不會觸發解壓縮,因為 sr 具有 rt 壓縮狀態旗標的超集。
為 layout、access 和 sync 定義之前和之後,讓我們擁有極大的彈性,能夠做到使用 ResourceBarriers 時無法做到的事情 (分割屏障除外,稍後會詳細說明),同時也改善了資源屏障可能導致不必要部分排清 (UAV 屏障),以及在最壞情況下可能導致記憶體損毀 (別名屏障,在不確定記憶體目前配置的情況下嘗試解壓縮時) 的情況。它也更符合 GPU 上同步處理的運作方式,因而讓開發人員能進行更精細的控制。
規格也定義了三種屏障類型:
- 全域屏障:
- 套用至所有記憶體。定義中不包含資源指標。
- 不會傳遞 layout 資訊作為引數。
- 緩衝區屏障
- 顧名思義,緩衝區屏障用於轉換緩衝區資源。
- 不會指定 layout 或子資源作為參數 (因為緩衝區兩者皆不使用)。
- 紋理屏障
- 用於轉換紋理資源 (rendertarget、depthstencil、srv 紋理、uav 紋理等)。
- 需要 layout,因此此屏障可以觸發解壓縮和壓縮。
- 可以指定特定子資源或範圍。
- 對於子資源,解壓縮方面有一些限制,因為我們無法只解壓縮資源的一部分。
- LayoutBefore, LayourAfter 屏障之前和之後的記憶體配置。將決定我們要壓縮/解壓縮的內容。僅由 D3D12_TEXTURE_BARRIER 使用。
- SyncBefore, SyncAfter 要等待排空的階段,以及在繼續之前要封鎖的階段。
- AccessBefore, AccessAfter 告知我們將轉換離開使用哪些快取,以及將轉換為使用哪些快取。
- Subresource range — 可定義屏障將轉換的子資源範圍。僅由 D3D12_TEXTURE_BARRIER 使用。
- Resource — 我們所轉換之記憶體的擁有者。由 D3D12_TEXTURE_BARRIER 和 D3D12_BUFFER_BARRIER 使用。
- Texture Barrier flags — 值可以是 D3D12_TEXTURE_BARRIER_FLAG_NONE 和 D3D12_TEXTURE_BARRIER_FLAG_DISCARD。僅由 D3D12_TEXTURE_BARRIER 使用。
- 我們在啟用資源時使用 discard 旗標,讓其中繼資料能正確初始化。
- 使用此旗標也可以批次處理資源初始化。清除作業可以重疊,且屏障同步作業會整合在一起。
進行設定
若要使用增強型屏障,您需要下列項目:
- 2023 年 3 月 GDK 或更新版本。
- ID3D12GraphicsCommandList7:呼叫 Barrier 方法所需。接受 D3D12_BARRIER_GROUP 結構和群組編號。
- 根據規格,支援增強型屏障 API 與舊版 D3D12_RESOURCE_STATES 之間的互通。不過,處於 D3D12_RESOURCE_STATE_COMMON 以外任何舊版狀態的子資源,在使用增強型屏障參照之前,必須先轉換為 D3D12_RESOURCE_STATE_COMMON。
- 在 Loose 資料夾 (部署目錄) 中建立一個空白檔案,並將其命名為 D3D__DisableLegacyResourceBarrierTranslation。
- 部署遊戲,並確認該檔案已部署到主機中。
- 注意,在此模式下執行時,驅動程式不支援混用舊版屏障與增強型屏障。
與 PC 規格的差異
- 在 XBOX 上,不會發出 ExecuteCommandList 邊界排清,因此不支援無屏障存取。
- 這是基於效能考量,因為完整的 GPU 排清成本很高。
- 規格中的無屏障存取
- 子資源。PC 會以子資源為單位追蹤中繼資料,而我們不會。這表示無法追蹤每個子資源的壓縮狀態。因此,涉及壓縮或解壓縮的 layout 變更,會因資源範圍是否涵蓋整個資源而產生不同的結果。
- 如果處於壓縮 layout 的資源有一或多個 (但非全部) 子資源轉換為未壓縮的 layout,整個資源都會變成未壓縮。
- 如果處於未壓縮 layout 的資源有一或多個 (但非全部) 子資源轉換為壓縮的 layout,整個資源會維持未壓縮。
- 我們確實支援深度/樣板的獨立壓縮狀態,這表示我們可以有壓縮的深度和未壓縮的樣板,反之亦然。
- 分割屏障可以運作,但在驅動程式上並非以分割屏障實作,而是轉換為立即屏障。我們之所以進行此轉換,是因為 D3D12 所定義的分割屏障會對每個資源造成記憶體負擔,而且在我們的硬體上效率不必要地低落。不過,轉換為立即屏障的效率並沒有達到理想程度。
- 這是因為驅動程式無法收到有關之前和之後 sync 的完整資訊。轉換為立即屏障是在屏障的近端進行 (遠端會被忽略),因此驅動程式必須採取悲觀做法,同時封鎖 CE 和 PFP,這可能使分割屏障比原生立即屏障更慢。
- 此外,從過去來看,很少有人在舊版上使用分割屏障。
- 未來解決每個資源記憶體負擔的一種方式,可能是允許開發人員自行提供此記憶體。
- 未來計劃重新檢視分割屏障的實作。
- 建議使用者在計算著色器上實作自己的紋理複製。當然,他們需要自行管理相依性,但這可以實現非序列化、更有效率的工作。
- 由驅動程式管理的紋理複製效能可能較差的原因之一,是紋理複製可能會使用 CB/DB 快取。這使得在紋理轉換進出複製時,需要額外進行 WB 以及使 CB 和 DB 快取失效。
增強型屏障範例
雖然透過驅動程式將舊版資源屏障呼叫轉譯為對等增強型屏障的能力,可以自動獲得執行階段效能改善,但直接使用增強型屏障可以進一步節省成本。此 API 的表達能力提供了減少同步處理延遲和消除不必要排清的機會。下列程式碼範例示範如何以增強型屏障 API 表示舊版資源屏障呼叫,以充分利用這些額外的優點。
轉換資源屏障
圖形應用程式中的記憶體資源經常在不同的情境下存取---無論是作為轉譯目標、著色器資源檢視、頂點緩衝區檢視,或處於其他類型的狀態。這導致經常需要轉換資源屏障,以確保正確的同步處理和快取一致性。不過,在某些情況下,轉換資源屏障可能會造成不必要的效能降低,例如過多的管線排清。 請考慮下列舊版資源屏障程式碼片段,其中顯示資源的狀態從著色器資源檢視 (SRV) 轉換為轉譯目標檢視 (RTV):


無序存取資源屏障
為了同步處理無序存取檢視 (UAV) 的讀取和寫入而建立的舊版資源屏障,也會因為與舊版轉換資源屏障中類似的驅動程式相關原因,而受到多餘 GPU 管線排清的影響。請考慮一個情境:從計算著色器產生 mip 對應紋理,全部寫入紋理 UAV 並從中讀取,並使用下列無序存取資源屏障程式碼來避免危害:

自我複製
以往,子資源無法同時進行寫入和讀取,因為舊版資源屏障設計不允許結合唯讀和寫入位元。這表示在資源中於兩個不重疊的區域之間移動資料時,需要建立暫存紋理、更多轉換屏障以在 D3D12_RESOURCE_STATE_COPY_SOURCE 和 D3D12_RESOURCE_STATE_COPY_DEST 之間切換,以及更多子資源複製作業。下方程式碼範例說明了將紋理左上四分之一複製到右下四分之一所需的工作。

別名資源屏障
如下方取自公開 D3D12 MSDN 文件的程式碼所示,在舊版資源屏障 API 中為放置的資源建立別名,過去只需要指定共用堆積記憶體的兩個資源。增強型屏障 API 有助於避免封鎖所有 GPU 工作和排清所有寫入的必要性,同時提供明確的 layout 狀態,以確保驅動程式不會解壓縮未壓縮的記憶體 (有關後一個問題的詳細資訊,請參閱此連結中的「Aliasing Barriers Are Very Expensive」)。與舊版資源屏障的相容性
先前的屏障 API (資源屏障) 仍受支援,但驅動程式會在底層將其轉譯為增強型屏障。我們已在 PIX 擷取上執行多項測試,以了解轉譯的影響 (相較於使用先前的實作)。結果顯示 EB 轉譯帶來顯著的效益。

值得注意的是,效益在很大程度上取決於圖形引擎在同步處理和屏障放置方面的建構方式。
轉譯相較於舊版資源屏障有所改善的範例:
範例 1 - UAV 屏障部分排清產生
舊版資源屏障

範例 2 - 別名
舊版資源屏障

以上內容顯示開發人員無需變更程式碼,單純透過增強型屏障即可獲得的效益。若要了解升級至新屏障 API 時的潛在效益,請參閱 增強型屏障範例 一節。
PIX 支援與驗證
PIX 同時支援增強型屏障和轉譯後的舊版資源屏障。 對於增強型屏障,會建立名為 Barrier 的 PIX 事件,其中保存所產生的 CP 封包。





驗證
增強型屏障和經過轉譯層的舊版資源屏障都可使用驗證。您獲得的驗證取決於您使用的 API:- 對於增強型屏障,驗證對等性是以其涵蓋的情境為準。增強型屏障與資源屏障不同,因此驗證不會一對一複製舊版的驗證內容,但會保證轉換的所有之前和之後屬性都正確。例如:
- 確保資源在開始轉換時處於正確的 layout。
- 驗證 API 的正確性 (詳見下文)。
- 對於舊版資源屏障 (轉譯後),驗證與目前資源屏障的驗證對等。
- 基礎裝置支援增強型屏障
- 不是組合命令清單
- 如果任何計數 (群組或每個群組的屏障) 為零,則發出警告
- 確認紋理屏障子資源符合紋理子資源界限
- 屏障類型符合資源類型
- LayoutBefore 和 AccessBefore 符合已知或假設的資源 layout 和 access。
- SyncBefore、AccessBefore 和 LayoutBefore 相容。
- SyncAfter、AccessAfter 和 LayoutAfter 相容。
- 結束分割屏障符合先前的開始分割屏障。
- 緩衝區和 D3D12_BARRIER_LAYOUT_COMMON 分割屏障不會跨越 ExecuteCommandLists 邊界。
- 由於舊版的容許情況,可能會是警告而非錯誤
