> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# D3D12 增強型屏障

> D3D12 增強型屏障

本文件旨在說明 XBOX Series X|S 的增強型屏障 (Enhanced Barriers) 實作，並提供深入的範例，說明如何充分運用這些屏障。Durango 不支援此功能。

## 內容清單

1. [簡介](#introduction)
2. [XBOX Series X|S 上的 GPU 同步處理](#gpu-synchronization-on-scarlett)
3. [深入了解屏障](#barriers-in-depth)
4. [進行設定](#setting-things-up)
5. [與 PC 規格的差異](#differences-with-the-pc-spec)
6. [增強型屏障範例](#enhanced-barrier-examples)
7. [與舊版資源屏障的相容性](#compatibility-with-legacy-resource-barriers)
8. [PIX 支援與驗證](#pix-support-and-validation)

<a id="introduction" />

## 簡介

***

「增強型屏障」(EB) 是一組新的 D3D12 API，旨在解決舊版資源屏障 API 所帶來的一些問題 (例如資源狀態提升與衰減等令人困惑的概念、別名屏障的設計問題、過多的排清作業等)，同時讓開發人員能夠獨立控制 GPU 工作同步處理。相較於舊版資源屏障，它們提供了表達能力更強的屏障類型。

增強型屏障的功能包括：

* 減少同步延遲與過多的快取排清。
* 不再有難以理解的提升與衰減規則。
* 快速、彈性的資源別名。
* 在屏障轉換期間捨棄。
* 同時讀取/寫入以及自我複製。

增強型屏障的 DirectX 規格連結可在[這裡](https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html)找到。

<a id="gpu-synchronization-on-scarlett" />

## XBOX Series X|S 上的 GPU 同步處理

***

在繼續了解增強型屏障之前，需要先介紹 XBOX Series X|S 上的同步處理。務必了解該節中說明的概念與機制，因為它們是屏障的基礎。

[XBOX Series X/S 上的 GPU 同步處理](/zh-TW/build/core-features/graphics/overviews/gpu-synchronization-scarlett)

現在我們可以深入探討增強型屏障，並更詳細地說明其運作方式。

<a id="barriers-in-depth" />

## 深入了解屏障

***

增強型屏障在決定如何轉換任何指定資源 (或記憶體) 時，引進了三個實用的概念。分別是 **Sync**、**Access** 和 **Layout**。在某種程度上，它們取代了資源屏障先前使用的**資源狀態**概念。不過它們並不相同，因為資源狀態一律可以表示為 Sync、Access 和 Layout 的組合，但 Sync、Access 和 Layout 的組合並不一定能表示為資源狀態。新的 API 表達能力更強。

**Sync (之前-之後)** 用於讓驅動程式知道我們需要等待哪個管線階段完成，以及在完成屏障作業之前應封鎖哪個引擎的執行。

* 下表顯示部分 sync 旗標如何轉譯為需要排空或封鎖之階段的需求 (請參閱管線化一節)。值得注意的是，部分屏障 sync 旗標指的是廣泛的階段 (例如複製)，但在內部，會根據 access 和 layout 提供的資料對應至特定階段 (複製來源/複製目的地)，進而產生更適當的排空/封鎖需求。此外，下表將以紋理的情況為例 (對於緩衝區，部分階段會有所不同)。

| **屏障 Sync 旗標** | **要排空的階段** | **要封鎖的階段** |
| - | - | - |
| D3D12\_BARRIER\_SYNC\_RENDER\_TARGET | Drain CB | BlockME |
| D3D12\_BARRIER\_SYNC\_COMPUTE\_SHADING | Drain CS | BlockME |
| D3D12\_BARRIER\_SYNC\_RAYTRACING | Drain CS | BlockME |
| D3D12\_BARRIER\_SYNC\_COPY (來源) | DrainCPDMA, DrainCS, DrainPS | BlockME |
| D3D12\_BARRIER\_SYNC\_COPY (目的地) | DrainCPDMA, DrainCS, DrainCB, DrainDB | BlockME |
| D3D12\_BARRIER\_SYNC\_DEPTH\_STENCIL | Drain DB | BlockME |
| D3D12XBOX\_BARRIER\_SYNC\_EXECUTE\_INDIRECT | DrainME | BlockPFP, BlockCE |

* 這些需求稍後會被剖析，並與其他基本類型一起傳遞給 SyncBottomOfPipe/SyncTopOfPipe 函式，以決定最終的同步處理動作 (例如 D3D12XBOX\_SYNC\_FLAG\_PS\_PARTIAL\_FLUSH 或 D3D12XBOX\_SYNC\_FLAG\_IDLE)。請勿將這些旗標與 D3D12\_BARRIER\_SYNC 旗標混淆，後者用於增強型屏障呼叫。
* 這些需求並不知道最終用來滿足它們的機制，因此會明確指出哪個硬體元件需要排空/封鎖。例如，後續元件可能會發現多個排空可以透過一個 EoP 事件完成。

**Access (之前-之後)** 告訴我們此資源在屏障之前和之後可能與哪些快取互動，進而轉譯為 WB 和失效需求。

* 在屏障的情境下，了解哪些資源對應至哪些快取很有用。例如，如果 AccessBefore 為 D3D12\_ACCESS\_RENDER\_TARGET，而之後的 access 為 D3D12\_ACCESS\_SHADER\_RESOURCE，我們會想知道要使哪些快取失效和/或排清 (如有必要)。下表顯示一些常用的資源類型及其使用的記憶體。
* 資源類型並不是決定資源使用哪些快取的唯一因素。作業類型 (純量與向量) 也很重要，而這是由編譯器決定的。例如，從常數緩衝區擷取時，如果是純量作業 (SMEM)，可以透過 \$K-L2 路徑；如果是向量作業 (VMEM)，則透過 L0-L1-L2。

| **Access 名稱** | **使用的快取** | **附註** |
| - | - | - |
| Rendertarget | CB 快取 - L1、L2 | |
| Depth/Stencil | DB 快取 - L1、L2 | |
| ShaderResource | L0-L1、\$K、L2、LM | |
| UnorderedAccess | L0-L1、\$K、L2、LM | |
| ConstantBuffer | L0-L1、\$K、L2 | 常數緩衝區通常透過 SMEM 指令讀取，但如果透過非一致索引存取，編譯器會產生 VMEM 指令。 |
| VertexBuffer | L0-L1、L2 | 頂點緩衝區透過 VMEM 指令讀取 (經由 L0-L1-L2)。 |
| IndexBuffer | L2 | 索引緩衝區透過由 PFP 觸發的 CPDMA 從 DRAM 擷取至 L2。 |
| CopyDest | L0-L1、\$K、L2、LM、CB 快取、DB 快取 | |
| CopySource | L0-L1、\$K、L2、LM | |
| 光線追蹤加速結構 (讀取/寫入) | L0-L1、\$K、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 旗標** | **非 TC** | **TC** |
| - | - | - |
| D3D12\_BARRIER\_LAYOUT\_COMMON | uncompressedAll | uncompressedAll |
| D3D12\_BARRIER\_LAYOUT\_GENERIC\_READ | uncompressedAll | uncompressedAll |
| D3D12\_BARRIER\_LAYOUT\_RENDER\_TARGET | CompressedColor CompressedDcc ScatteredFmask IndirectColorClear | CompressedColor CompressedDcc ScatteredFmask IndirectColorClear |
| D3D12\_BARRIER\_LAYOUT\_UNORDERED\_ACCESS | uncompressedAll | CompressedColor CompressedDepth CompressedStencil CompressedDcc ScatteredFmask |
| D3D12\_BARRIER\_LAYOUT\_SHADER\_RESOURCE | uncompressedAll | CompressedColor CompressedDcc ScatteredFmask CompressedDepth CompressedStencil |
| D3D12\_BARRIER\_LAYOUT\_COPY\_SOURCE | CompressedColor CompressedDcc | CompressedColor CompressedDcc ScatteredFmask IndirectColorClear CompressedDepth CompressedStencil |
| D3D12\_BARRIER\_LAYOUT\_COPY\_DEST | CompressedColor CompressedDcc | CompressedColor CompressedDepth CompressedStencil CompressedDcc ScatteredFmask IndirectColorClear |

為 **layout**、**access** 和 **sync** 定義之前和之後，讓我們擁有極大的彈性，能夠做到使用 ResourceBarriers 時無法做到的事情 (分割屏障除外，稍後會詳細說明)，同時也改善了資源屏障可能導致不必要部分排清 (UAV 屏障)，以及在最壞情況下可能導致記憶體損毀 (別名屏障，在不確定記憶體目前配置的情況下嘗試解壓縮時) 的情況。它也更符合 GPU 上同步處理的運作方式，因而讓開發人員能進行更精細的控制。

### 規格也定義了三種屏障類型：

* 全域屏障：
  * 套用至所有記憶體。定義中不包含資源指標。
  * 不會傳遞 layout 資訊作為引數。
* 緩衝區屏障
  * 顧名思義，緩衝區屏障用於轉換緩衝區資源。
  * 不會指定 layout 或子資源作為參數 (因為緩衝區兩者皆不使用)。
* 紋理屏障
  * 用於轉換紋理資源 (rendertarget、depthstencil、srv 紋理、uav 紋理等)。
  * 需要 layout，因此此屏障可以觸發解壓縮和壓縮。
  * 可以指定特定子資源或範圍。
    * 對於子資源，解壓縮方面有一些限制，因為我們無法只解壓縮資源的一部分。

以下是三種屏障類型的 D3D12 API 定義：

```cpp theme={null}
struct D3D12_BUFFER_BARRIER 
{ 
    D3D12_BARRIER_SYNC SyncBefore; 
    D3D12_BARRIER_SYNC SyncAfter; 
    D3D12_BARRIER_ACCESS AccessBefore; 
    D3D12_BARRIER_ACCESS AccessAfter; 
    ID3D12Resource *pResource;     
    UINT64 Offset;  // Must be 0 
    UINT64 Size;    // UINT64_MAX or size in bytes 
};  
```

```cpp theme={null}
struct D3D12 _GLOBAL_BARRIER 
{ 
    D3D12_BARRIER_SYNC SyncBefore; 
    D3D12_BARRIER_SYNC SyncAfter; 
    D3D12_BARRIER_ACCESS AccessBefore; 
    D3D12_BARRIER_ACCESS AccessAfter; 
}  
```

```cpp theme={null}
struct D3D12_TEXTURE_BARRIER 
{ 
    D3D12_BARRIER_SYNC SyncBefore; 
    D3D12_BARRIER_SYNC SyncAfter; 
    D3D12_BARRIER_ACCESS AccessBefore; 
    D3D12_BARRIER_ACCESS AccessAfter; 
    D3D12_BARRIER_LAYOUT LayoutBefore; 
    D3D12_BARRIER_LAYOUT LayoutAfter; 
    ID3D12Resource *pResource; 
    D3D12_BARRIER_SUBRESOURCE_RANGE Subresources; 
    D3D12_TEXTURE_BARRIER_FLAGS Flags; 
};
```

* **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 旗標，讓其中繼資料能正確初始化。
  * 使用此旗標也可以批次處理資源初始化。清除作業可以重疊，且屏障同步作業會整合在一起。

<a id="setting-things-up" />

## 進行設定

***

若要使用增強型屏障，您需要下列項目：

* 2023 年 3 月 GDK 或更新版本。
* ID3D12GraphicsCommandList7：呼叫 [Barrier](https://learn.microsoft.com/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist7-barrier) 方法所需。接受 D3D12\_BARRIER\_GROUP 結構和群組編號。

(**選用**) ID3D12Device10 可存取新的資源建立定義：**CreatePlacedResource2**、**CreateCommitedResource3** 和 **CreateReservedResource2**。這些定義接受 D3D12\_BARRIER\_LAYOUT 作為參數，而不是 D3D12\_RESOURCE\_STATE。

* 根據規格，支援增強型屏障 API 與舊版 D3D12\_RESOURCE\_STATES 之間的互通。不過，處於 D3D12\_RESOURCE\_STATE\_COMMON 以外任何舊版狀態的子資源，在使用增強型屏障參照之前，必須先轉換為 D3D12\_RESOURCE\_STATE\_COMMON。

(**選用**) ID3D12Device11 可存取用於資源建立的新擴充 API：**CreatePlacedResourceX1** 和 **CreateComponentPlacedResourceX2**。這些 API 接受 D3D12\_BARRIER\_LAYOUT 作為參數，而不是 D3D12\_RESOURCE\_STATE。

**使用舊版資源屏障進行測試**

我們提供一個機制，可用來關閉 EB 並回到先前的資源屏障實作。如果開發人員在任何時候遇到停止回應，或發現屏障的行為不如預期 (例如某個轉換所需時間比平常長，或產生錯誤的同步處理/解壓縮旗標)，可能值得回頭檢查問題是否持續存在。所需步驟如下：

* 在 Loose 資料夾 (部署目錄) 中建立一個空白檔案，並將其命名為 **D3D\_\_DisableLegacyResourceBarrierTranslation**。
* 部署遊戲，並確認該檔案已部署到主機中。
  * **注意**，在此模式下執行時，驅動程式不支援混用舊版屏障與增強型屏障。

如果開發人員發現增強型屏障有任何此類問題，**強烈**建議透過 [XBOX 開發人員論壇](https://forums.xboxlive.com/)與我們聯絡。

<a id="differences-with-the-pc-spec" />

## 與 PC 規格的差異

***

1. **在 XBOX 上，不會發出 ExecuteCommandList 邊界排清**，因此不支援無屏障存取。
   * 這是基於效能考量，因為完整的 GPU 排清成本很高。
   * [規格中的無屏障存取](https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html#barrier-free-access)
2. **子資源**。PC 會以子資源為單位追蹤中繼資料，而我們不會。這表示無法追蹤每個子資源的壓縮狀態。因此，涉及壓縮或解壓縮的 layout 變更，會因資源範圍是否涵蓋整個資源而產生不同的結果。
   * 如果處於壓縮 layout 的資源有一或多個 (但非全部) 子資源轉換為未壓縮的 layout，整個資源都會變成未壓縮。
   * 如果處於未壓縮 layout 的資源有一或多個 (但非全部) 子資源轉換為壓縮的 layout，整個資源會維持未壓縮。
   * 我們確實支援深度/樣板的獨立壓縮狀態，這表示我們可以有壓縮的深度和未壓縮的樣板，反之亦然。
3. **分割屏障**可以運作，但在驅動程式上並非以分割屏障實作，而是轉換為立即屏障。我們之所以進行此轉換，是因為 D3D12 所定義的分割屏障會對每個資源造成記憶體負擔，而且在我們的硬體上效率不必要地低落。不過，轉換為立即屏障的效率並沒有達到理想程度。
   * 這是因為驅動程式無法收到有關之前和之後 sync 的完整資訊。轉換為立即屏障是在屏障的近端進行 (遠端會被忽略)，因此驅動程式必須採取悲觀做法，同時封鎖 CE 和 PFP，這可能使分割屏障比原生立即屏障更慢。
   * 此外，從過去來看，很少有人在舊版上使用分割屏障。
   * 未來解決每個資源記憶體負擔的一種方式，可能是允許開發人員自行提供此記憶體。
   * 未來計劃重新檢視分割屏障的實作。

雖然不算是差異，但值得一提的是**複製仍然是序列化的** (規格提到使用增強型屏障後，可能不再需要序列化複製)。**注意**：將 D3D12XBOX\_CMDLIST\_DRIVER\_HINT\_DONT\_SYNC\_ON\_COPY 作為命令清單提示傳遞，即可進行非序列化的清除和複製。

* 建議使用者在計算著色器上實作自己的紋理複製。當然，他們需要自行管理相依性，但這可以實現非序列化、更有效率的工作。
  * 由驅動程式管理的紋理複製效能可能較差的原因之一，是紋理複製可能會使用 CB/DB 快取。這使得在紋理轉換進出複製時，需要額外進行 WB 以及使 CB 和 DB 快取失效。

<a id="enhanced-barrier-examples" />

## 增強型屏障範例

***

雖然透過驅動程式將舊版資源屏障呼叫轉譯為對等增強型屏障的能力，可以自動獲得執行階段效能改善，但直接使用增強型屏障可以進一步節省成本。此 API 的表達能力提供了減少同步處理延遲和消除不必要排清的機會。下列程式碼範例示範如何以增強型屏障 API 表示舊版資源屏障呼叫，以充分利用這些額外的優點。

* [轉換資源屏障](#transition-resource-barriers)
* [無序存取資源屏障](#unordered-access-resource-barriers)
* [自我複製](#self-copy)
* [別名資源屏障](#aliasing-resource-barriers)

<a id="transition-resource-barriers" />

### **轉換資源屏障**

圖形應用程式中的記憶體資源經常在不同的情境下存取---無論是作為轉譯目標、著色器資源檢視、頂點緩衝區檢視，或處於其他類型的狀態。這導致經常需要轉換資源屏障，以確保正確的同步處理和快取一致性。不過，在某些情況下，轉換資源屏障可能會造成不必要的效能降低，例如過多的管線排清。

請考慮下列舊版資源屏障程式碼片段，其中顯示資源的狀態從著色器資源檢視 (SRV) 轉換為轉譯目標檢視 (RTV)：

```cpp theme={null}
auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(renderTarget,
    D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
    D3D12_RESOURCE_STATE_RENDER_TARGET);
commandList->ResourceBarrier(1, &barrier);
```

此範例先繪製到 RTV，將資源轉換為 SRV 以供計算著色器使用，然後再將其轉換回 RTV，讓下一個畫面執行相同的使用模式。由於沒有明確的方式可以描述資源上次的存取方式，以及需要同步處理哪種 GPU 工作，驅動程式需要排清資源可能曾處於 D3D12\_RESOURCE\_STATE\_NON\_PIXEL\_SHADER\_RESOURCE 狀態的所有管線階段。

如下方 PIX 擷取所示，此轉換如預期般使驅動程式叫用計算著色器部分排清以處理最後一次分派，但也不必要地觸發了頂點著色器部分排清。此資源曾用於先前的繪製呼叫和分派，而驅動程式不知道資源在每種情況下所使用的狀態。它也不知道在發出屏障時繪製和分派是否已完成。因此，為了優先確保正確的同步處理，它必須假設使用該資源的所有使用中非像素著色器管線階段 (頂點和計算) 都可能受到資源存取類型變更的影響。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_01.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=feb23c0da6682c3b650d4c6ef9302033" alt="Graphical user interface, application Description automatically generated" width="1100" height="690" data-path="images/gdk/features/graphics/EnhancedBarriers_01.png" />

如上方 PIX 擷取所示，頂點著色器部分排清的成本為 0 us，因為目前沒有其他繪製正在執行。不過，如果有任何來自不相關之先前 D3D 事件的頂點著色器工作，資源屏障可能需要更長的時間才能完成。

這可以透過使用增強型屏障 API 額外指定資源的同步處理和 layout 狀態來避免，如下方醒目提示的引數所示。使用者可以保證在屏障呼叫期間不會有使用中的頂點著色器工作，並明確將部分排清限制在計算著色階段，同時只封鎖要求在 RTV 上運作的工作。

```cpp theme={null}
auto barrierSubresourceRange = CD3DX12_BARRIER_SUBRESOURCE_RANGE();

auto textureBarrier = CD3DX12_TEXTURE_BARRIER (
    D3D12_BARRIER_SYNC_COMPUTE_SHADING,
    D3D12_BARRIER_SYNC_RENDER_TARGET,
    D3D12_BARRIER_ACCESS_SHADER_RESOURCE,
    D3D12_BARRIER_ACCESS_RENDER_TARGET,
    D3D12_BARRIER_LAYOUT_SHADER_RESOURCE,
    D3D12_BARRIER_LAYOUT_RENDER_TARGET,
    renderTarget,
    barrierSubresourceRange
);

auto barrierGroup = CD3DX12_BARRIER_GROUP(1, &textureBarrier);
```

如下所示，增強型屏障現在省略了頂點著色器部分排清。PIX 中的命令反組譯檢視和事件詳細資料檢視，也可分別用來更詳細地檢視命令封包和屏障設定資訊：

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_02.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=3ca408d5ea63002ae2ccff6ecb8bbfa7" alt="Graphical user interface, text, application, email Description automatically generated" width="750" height="391" data-path="images/gdk/features/graphics/EnhancedBarriers_02.png" />

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_03.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=3bf3a236e5f689fcaa10dc0626b80524" alt="Graphical user interface, application, table Description automatically generated" width="750" height="556" data-path="images/gdk/features/graphics/EnhancedBarriers_03.png" />

如果資源是緩衝區，而不是像先前案例中的 2D 紋理，且緩衝區要用作串流輸出而非 RTV，則可以類似方式使用緩衝區屏障：

```cpp theme={null}
auto bufferBarrier = CD3DX12_BUFFER_BARRIER (
    D3D12_BARRIER_SYNC_COMPUTE_SHADING,
    D3D12_BARRIER_SYNC_STREAM_OUTPUT,
    D3D12_BARRIER_ACCESS_SHADER_RESOURCE,
    D3D12_BARRIER_ACCESS_STREAM_OUTPUT,
    m_renderTarget.Get(),
    barrierSubresourceRange
);
```

<a id="unordered-access-resource-barriers" />

### **無序存取資源屏障**

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

```cpp theme={null}
auto barrier = CD3DX12_RESOURCE_BARRIER::UAV(texture0);
commandList->ResourceBarrier(1, &barrier);
```

查看 PIX 中產生的硬體事件，會發現針對像素著色器、頂點著色器和計算著色器階段都執行了部分排清。在執行屏障之前只分派了計算工作項目，因此計算著色器部分排清就已足夠。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_04.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=38b89f5d74fede1bba85870254f2ad07" alt="Graphical user interface, application, table Description automatically generated" width="1100" height="969" data-path="images/gdk/features/graphics/EnhancedBarriers_04.png" />

這裡的解決方案與轉換資源屏障範例非常類似，就是使用紋理屏障，讓同步處理狀態只排清和封鎖計算著色器工作。下列程式碼修改會使頂點和像素著色器部分排清從 PIX 擷取中消失。

```cpp theme={null}
auto barrierSubresourceRange = CD3DX12_BARRIER_SUBRESOURCE_RANGE();

auto barrier = CD3DX12_TEXTURE_BARRIER (
    D3D12_BARRIER_SYNC_CLEAR_UNORDERED_ACCESS_VIEW,
    D3D12_BARRIER_SYNC_COMPUTE_SHADING,
    D3D12_BARRIER_ACCESS_UNORDERED_ACCESS
    D3D12_BARRIER_ACCESS_SHADER_RESOURCE,
    D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS,
    D3D12_BARRIER_LAYOUT_SHADER_RESOURCE,
    texture0,
    barrierSubresourceRange
);

auto barrierGroup = CD3DX12_BARRIER_GROUP(1, &barrier);
```

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_05.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=ceb9f67d854cd57cc87cebceb8913d3e" alt="Graphical user interface, application, table Description automatically generated" width="750" height="445" data-path="images/gdk/features/graphics/EnhancedBarriers_05.png" />

<a id="self-copy" />

### **自我複製**

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

```cpp theme={null}
// Legacy resource barriers
{
    auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(texture,
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
        D3D12_RESOURCE_STATE_COPY_SOURCE
    );
    commandList->ResourceBarrier(1, &barrier);

    barrier = CD3DX12_RESOURCE_BARRIER::Transition(textureTmp,
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
        D3D12_RESOURCE_STATE_COPY_DEST
    );
    commandList->ResourceBarrier(1, &barrier);
}

// Copy top left quarter to temp texture
{
    auto locationSrc = CD3DX12_TEXTURE_COPY_LOCATION(texture, 0);
    auto locationDst = CD3DX12_TEXTURE_COPY_LOCATION(textureTmp, 0);
    commandList->CopyTextureRegion(&locationDst, boxTopLeft.left,
        boxTopLeft.top, boxTopLeft.front, &locationSrc, &boxTopLeft);

    auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(texture,
        D3D12_RESOURCE_STATE_COPY_SOURCE,
        D3D12_RESOURCE_STATE_COPY_DEST
    );
    commandList->ResourceBarrier(1, &barrier);

    barrier = CD3DX12_RESOURCE_BARRIER::Transition(textureTmp,
        D3D12_RESOURCE_STATE_COPY_DEST,
        D3D12_RESOURCE_STATE_COPY_SOURCE
    );
    commandList->ResourceBarrier(1, &barrier);
}

// Copy temp texture to bottom right quarter
{
    auto locationSrc = CD3DX12_TEXTURE_COPY_LOCATION(textureTmp, 0);
    auto locationDst = CD3DX12_TEXTURE_COPY_LOCATION(texture, 0);
    commandList->CopyTextureRegion(&locationDst, boxBottomRight.left,
        boxBottomRight.top, boxBottomRight.front, &locationSrc, &boxTopLeft);

    auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(texture,
        D3D12_RESOURCE_STATE_COPY_DEST,
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS
    );
    commandList->ResourceBarrier(1, &barrier);

    barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_textureTmp.Get(),
        D3D12_RESOURCE_STATE_COPY_SOURCE,
        D3D12_RESOURCE_STATE_UNORDERED_ACCESS
    );
    commandList->ResourceBarrier(1, &barrier);
}
```

增強型屏障 API 現在允許自我複製，讓所需的程式碼更容易閱讀和撰寫，同時不再需要中繼紋理：

```cpp theme={null}
// Self copy top left quarter to bottom right quarter
auto locationSrc = CD3DX12_TEXTURE_COPY_LOCATION(texture, 0);
auto locationDst = CD3DX12_TEXTURE_COPY_LOCATION(texture, 0);

commandList->CopyTextureRegion(&locationDst, boxBottomRight.left,
    boxBottomRight.top, boxBottomRight.front, &locationSrc, &boxTopLeft);
```

PIX 的事件檢視可用來視覺化舊版資源屏障與增強型屏障在紋理自我複製實作上的硬體事件差異。請參考下列兩張 PIX 擷取的螢幕擷取畫面，第一張只包含舊版資源屏障，第二張則使用增強型屏障：

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_06.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=40e35c9cfcfc4fa549e5b79574a96fad" alt="Table Description automatically generated" width="926" height="1100" data-path="images/gdk/features/graphics/EnhancedBarriers_06.png" />

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_07.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=8145f6ff6b0ffaabb5a5032722f1693d" alt="Graphical user interface, text, application, email Description automatically generated" width="750" height="248" data-path="images/gdk/features/graphics/EnhancedBarriers_07.png" />

<a id="aliasing-resource-barriers" />

### **別名資源屏障**

如下方取自[公開 D3D12 MSDN 文件](https://learn.microsoft.com/windows/win32/api/d3d12/ns-d3d12-d3d12_resource_aliasing_barrier)的程式碼所示，在舊版資源屏障 API 中為放置的資源建立別名，過去只需要指定共用堆積記憶體的兩個資源。增強型屏障 API 有助於避免封鎖所有 GPU 工作和排清所有寫入的必要性，同時提供明確的 layout 狀態，以確保驅動程式不會解壓縮未壓縮的記憶體 (有關後一個問題的詳細資訊，請參閱[此連結](https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html#aliasing-barriers-are-very-expensive)中的「Aliasing Barriers Are Very Expensive」)。

```cpp theme={null}
typedef struct D3D12_RESOURCE_ALIASING_BARRIER 
{
    ID3D12Resource *pResourceBefore;
    ID3D12Resource *pResourceAfter;
} D3D12_RESOURCE_ALIASING_BARRIER;
```

此程式碼片段示範如何使用紋理屏障來促進兩個放置資源之間的別名 -- pTex0 保存先前從上傳堆積複製過來的資料，而 pTex1 重複使用該資料以複製回另一個緩衝區。

```cpp theme={null}
// Transition pTex0 to COPY_SOURCE
auto tex0Barrier = CD3DX12_TEXTURE_BARRIER (
    D3D12_BARRIER_SYNC_RENDER_TARGET,
    D3D12_BARRIER_SYNC_COPY,
    D3D12_BARRIER_ACCESS_RENDER_TARGET,
    D3D12_BARRIER_ACCESS_COPY_SOURCE,
    D3D12_BARRIER_LAYOUT_RENDER_TARGET,
    D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE,
    pTex0,
    CD3DX12_BARRIER_SUBRESOURCE_RANGE(0, 1, 0, 1)
);

// Barrier to affect an aliasing transition from pTex0 to
// pTex1 with a layout of COPY_SOURCE
auto tex1Barrier = CD3DX12_TEXTURE_BARRIER (
    D3D12_BARRIER_SYNC_RENDER_TARGET,
    D3D12_BARRIER_SYNC_COPY,
    D3D12_BARRIER_ACCESS_NO_ACCESS,
    D3D12_BARRIER_ACCESS_COPY_SOURCE,
    D3D12_BARRIER_LAYOUT_UNDEFINED,
    D3D12_BARRIER_LAYOUT_COPY_SOURCE,
    pTex1,
    CD3DX12_BARRIER_SUBRESOURCE_RANGE(0, 1, 0, 1)
);
```

<a id="compatibility-with-legacy-resource-barriers" />

## 與舊版資源屏障的相容性

***

先前的屏障 API (資源屏障) 仍受支援，但驅動程式會在底層將其**轉譯**為增強型屏障。我們已在 PIX 擷取上執行多項測試，以了解轉譯的影響 (相較於使用先前的實作)。結果顯示 EB 轉譯帶來顯著的效益。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_08.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=7117c2331d6b4da6e8de894026bbc3f2" alt="Chart, line chart Description automatically generated" width="1223" height="689" data-path="images/gdk/features/graphics/EnhancedBarriers_08.png" />

上圖顯示總共 115 個擷取的單一畫面擷取效益 (以畫面時間改善百分比表示)。值得注意的是，此處的選擇涵蓋遊戲與範例，其中範例顯示的效益較高。以下是 AAA 遊戲清單，以及我們從其 PIX 擷取中獲得的畫面時間改善 (%)。

| 遊戲 | 畫面時間改善 (%) |
| - | - |
| 遊戲 A | 5.7% |
| 遊戲 B | 3.7% |
| 遊戲 C | 3.2% |
| 遊戲 D | 2.5% |
| 遊戲 E | 2.4% |
| 遊戲 F | 2.3% |
| 遊戲 G | 1.6% |

值得注意的是，效益在很大程度上取決於圖形引擎在同步處理和屏障放置方面的建構方式。

轉譯相較於舊版資源屏障有所改善的範例：

### **範例 1 - UAV 屏障部分排清產生**

舊版資源屏障

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_09.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=f6c371681e9f7d80af7d70d03a23773a" alt="Graphical user interface" width="455" height="150" data-path="images/gdk/features/graphics/EnhancedBarriers_09.png" />

轉譯後的資源屏障

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_10.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=acf5eca179145aca94b75c08225e6423" alt="Graphical user interface" width="426" height="179" data-path="images/gdk/features/graphics/EnhancedBarriers_10.png" />

轉譯後的屏障顯示了正確的同步步驟。它在失效之前會執行 PS、VS 和 CS 部分排清，因為它必須為最壞的情況做好準備 (考量所有可能寫入 UAV 的階段)。另一方面，舊版屏障在失效之前只觸發 PS 和 CS 部分排清。這並不正確，因為它假設 VS 階段之後必定會接著 PS，但情況並非總是如此。**在此範例中，轉譯後的屏障修正了 UAV 資源屏障中存在的問題**。

### **範例 2 - 別名**

舊版資源屏障

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_11.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=fa96c405ce385338f87f5851ae3e1be6" alt="Graphical user interface" width="1342" height="371" data-path="images/gdk/features/graphics/EnhancedBarriers_11.png" />

轉譯後的資源屏障

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_12.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=e50298acca609ad108f9a55d080ead6c" alt="Graphical user interface" width="1029" height="122" data-path="images/gdk/features/graphics/EnhancedBarriers_12.png" />

在此範例中，ResourceBarrier::Aliasing 產生了一些不必要的 streamout 排清。轉譯後的版本會針對一些 BoP 排清和失效進行柵欄等待，接著進行一些 ToP 同步處理 (這與舊版的做法相同，只是沒有與 streamout 相關的封包)。**在此範例中，轉譯後的屏障從轉譯中獲得了效能提升**。

> 以上內容顯示開發人員無需變更程式碼，單純透過增強型屏障即可獲得的效益。若要了解升級至新屏障 API 時的潛在效益，請參閱 [**增強型屏障範例**](#enhanced-barrier-examples) 一節。

<a id="pix-support-and-validation" />

## PIX 支援與驗證

***

**PIX** 同時支援增強型屏障和轉譯後的舊版資源屏障。

**對於增強型屏障**，會建立名為 **Barrier** 的 PIX 事件，其中保存所產生的 CP 封包。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_13.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=4bff7bc7ae85abc391ca2d7d705ab5a0" alt="Image of a SRV to UAV buffer barrier in pix" width="594" height="114" data-path="images/gdk/features/graphics/EnhancedBarriers_13.png" />

在事件詳細資料檢視中，您可以取得 Barrier API 呼叫的所有詳細資料，例如類型、屏障群組計數、sync、access 和 layout (如果是紋理屏障) 的之前/之後，以及 pResource (如果不是全域屏障)。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_14.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=324c1b88b935cdee6166bb371d6c950e" alt="Graphical user interface, text Description automatically generated" width="1023" height="754" data-path="images/gdk/features/graphics/EnhancedBarriers_14.png" />

**對於資源屏障 (舊版)**，會顯示 ResourceBarrier API 名稱，並以子節點顯示轉譯後的屏障 (\[Barrier])。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_15.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=3816626e092ebf565bc71b746be4e77c" alt="Graphical user interface, text, application, website Description automatically generated" width="778" height="142" data-path="images/gdk/features/graphics/EnhancedBarriers_15.png" />

選取 ResourceBarrier 事件時，您可以看到舊版的事件詳細資料 (與增強型屏障推出前資源屏障的事件詳細資料檢視相同，只是 Actions 位元會顯示為 D3D12XBOX\_RESOURCE\_BARRIER\_ACTION\_NONE)。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_16.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=60ae625af8392910661bb0e243d863fc" alt="A screenshot of a computer Description automatically generated with medium confidence" width="877" height="577" data-path="images/gdk/features/graphics/EnhancedBarriers_16.png" />

選取 \[barrier] 子節點，即可看到轉譯後屏障的事件詳細資料 (就像增強型屏障一樣)。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/EnhancedBarriers_17.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=e9c824b9f2c87a30e721fea5af7affc3" alt="Graphical user interface, text, application Description automatically generated" width="868" height="157" data-path="images/gdk/features/graphics/EnhancedBarriers_17.png" />

### **驗證**

**增強型屏障**和**經過轉譯層的舊版資源屏障**都可使用驗證。您獲得的驗證取決於您使用的 API：

* 對於增強型屏障，驗證對等性是以其涵蓋的情境為準。增強型屏障與資源屏障不同，因此驗證不會一對一複製舊版的驗證內容，但會保證轉換的所有之前和之後屬性都正確。例如：
  * 確保資源在開始轉換時處於正確的 layout。
  * 驗證 API 的正確性 (詳見下文)。
* 對於舊版資源屏障 (轉譯後)，驗證與目前資源屏障的驗證對等。

驗證也會檢查從舊版屏障轉換為增強型屏障的資源是否處於正確的狀態 (D3D12\_RESOURCE\_STATE\_COMMON)。

**對於 API 驗證**，規格列出了預期的內容。偵錯層會在 Barrier 呼叫期間驗證下列項目：

* 基礎裝置支援增強型屏障
* 不是組合命令清單
* 如果任何計數 (群組或每個群組的屏障) 為零，則發出警告
* 確認紋理屏障子資源符合紋理子資源界限
* 屏障類型符合資源類型
* LayoutBefore 和 AccessBefore 符合已知或假設的資源 layout 和 access。
* SyncBefore、AccessBefore 和 LayoutBefore 相容。
* SyncAfter、AccessAfter 和 LayoutAfter 相容。
* 結束分割屏障符合先前的開始分割屏障。
* 緩衝區和 D3D12\_BARRIER\_LAYOUT\_COMMON 分割屏障不會跨越 ExecuteCommandLists 邊界。
  * 由於舊版的容許情況，可能會是警告而非錯誤

至於驗證輸出，將遵循下列格式：

```cpp theme={null}
TextD3D12 ERROR: [0xA7900038] ID3D12CommandQueue(Graphics)::ExecuteCommandLists: Resource is being transitioned from legacy to enhanced barriers but is not in the state D3D12_RESOURCE_STATE_COMMON as is required.
Current State is: D3D12_RESOURCE_STATE_RENDER_TARGET
|  -Group Index:   0
|  -Barrier Index: 0
|  -Barrier
|   |  -SyncBefore:   D3D12_BARRIER_SYNC_RENDER_TARGET
|   |  -SyncAfter:    D3D12_BARRIER_SYNC_COMPUTE_SHADING
|   |  -AccessBefore: D3D12_BARRIER_ACCESS_RENDER_TARGET
|   |  -AccessAfter:  D3D12_BARRIER_ACCESS_UNORDERED_ACCESS
|   |  -LayoutBefore: D3D12_BARRIER_LAYOUT_RENDER_TARGET
|   |  -LayoutAfter:  D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS
|   |  -pResource:    (0x0000000200200C00) "<untitled>"
|   |  -Subresources
|   |    |  -IndexOrFirstMipLevel: 4294967295
|   |    |  -NumMipLevels:         0
|   |    |  -FirstArraySlice:      0
|   |    |  -NumArraySlices:       0
|   |    |  -FirstPlane:           0
|   |    |  -NumPlanes:            0
|   |  -Flags: D3D12_TEXTURE_BARRIER_FLAG_NONE (0x00000000)
```


## Related topics

- [D3D12_TEXTURE_BARRIER (XBOX Series consoles)](/zh-CN/reference/graphics/d3d12/structs/d3d12_texture_barrier_public.md)
- [D3D12_GLOBAL_BARRIER (XBOX Series 主机)](/zh-CN/reference/graphics/d3d12/structs/d3d12_global_barrier_public.md)
- [D3D12_BUFFER_BARRIER (XBOX Series consoles)](/zh-CN/reference/graphics/d3d12/structs/d3d12_buffer_barrier_public.md)
- [D3D12_FEATURE_DATA_D3D12_OPTIONS12](/zh-CN/reference/graphics/d3d12/structs/d3d12_feature_data_d3d12_options12_public.md)
- [D3D12_RESOURCE_BARRIER](/zh-CN/reference/graphics/d3d12/structs/d3d12_resource_barrier_public.md)
