> ## 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.

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

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

討論 GPU 同步處理時，有兩個重要概念：管線化與快取一致性。簡單來說，GPU 可視為由許多階段組成的管線，繪製呼叫與計算分派從命令處理器進入，並在執行完成後退出。這就需要一些機制，以在多個工作負載需要存取相同記憶體時確保正確性。如果產生者寫入某個記憶體位址，而一個 (或多個) 取用者嘗試從該位址讀取，我們希望確保產生者在取用者開始讀取之前已完成寫入。[管線化](#pipelining)說明用來避免工作負載重疊的許多機制。

GPU 的另一個重要細節是，不同的硬體區塊擁有各自的記憶體快取，而這些快取並不一致 (它們不會自動同步或互相窺探)。回到先前的範例，如果產生者在某個 GPU 區塊上寫入記憶體之後，取用者在另一個區塊上讀取該記憶體位址，就需要有方法確保該記憶體的變更對所有取用者都可見。[快取一致性](#cache-coherency)說明可用來確保這一點的機制。

<a id="Pipelining" />

## 管線化

若要實作正確且最有效率的同步處理，務必了解 XBOX Series X|S GPU 的硬體區塊，從它接收到起始動作的命令，到完成執行該動作為止。命令處理器 (CP) 是 GPU 中從記憶體接收命令 (簡單來說，就是我們推送到 D3D12CommandLists 的命令) 的區塊，命令以封包形式傳遞 (可在 PIX 命令反組譯檢視中檢視)。CP 是一個由許多部分組成的複雜區塊，但我們可以將其簡化為三個部分：

* 命令處理器擷取器 (CPF)，負責從記憶體擷取封包，並將其傳送至 CPG 或 CPC 進一步處理。
* 命令處理器圖形 (CPG)，負責管理和處理圖形管道的命令。
* 計算命令處理器 (CPC)，與 CPG 平行執行，負責管理和處理計算管道的命令 (由 Async Compute 使用)。

在 CPG 中，預先擷取剖析器 (PFP) 會接收封包，並加以執行或透過 FIFO 佇列傳送至微引擎 (ME)。對於繪製和分派，ME 會向著色器處理單元 (SPI，負責處理著色器工作執行的單元) 發出訊號，以起始繪製/分派。繪製/分派完成後 (表示最後一個 wave 已執行完畢，且就繪製而言，算繪後端也已完成其工作)，即到達管線的末端。

對於 CPC，封包會由計算微引擎 (MEC) 擷取，並由其向 SPI 發出訊號以起始分派。

**為了協助視覺化，以下是簡化的圖表**

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

* PFP 負責擷取索引緩衝區資料、處理間接引數、述詞資訊，以及與 ME 同步。
* ME 可以觸發排清、起始繪製/分派，並具有 CPDMA 位置來起始複製。
* 常數引擎 (CE) 是 CPG 內部的一個區塊，具有用於儲存每個繪製命令所需常數的 RAM。它會將這些常數複製到 L2，然後在資料就緒時向 ME 發出訊號 (讓 ME 可以暫停繪製命令，直到常數就位)。ME 也可以在繪製完成時向 CE 發出訊號，讓 CE 可以重複使用其資源。
* CP 直接記憶體存取 (CPDMA) 負責執行複製 (從記憶體/暫存器複製或複製到記憶體/暫存器)，並可由 PFP、ME 或 MEC 觸發。它可以從任何虛擬記憶體複製，或複製到任何虛擬記憶體。

我們也可以插入可程式化的事件，在管線的不同部分執行：

* 著色器結束事件 (EoS) 會在分派/繪製的所有 wave 完成後執行，但不會等待算繪後端完成 (在固定管線的情況下)。
* 管道底部 (BoP) 是指所有著色器和算繪後端工作都已完成時，但不保證記憶體寫入已完成。
  * 對於計算而言，EoS 和 BoP 是相同的，因為沒有算繪後端工作。

如前所述，需要一些機制來確保有記憶體衝突的工作負載不會平行執行：

* 柵欄 (Fence)
  * 由 (至少) 兩個封包組成的同步處理基本類型。第一個封包會在記憶體位置寫入**參考值**。此封包是一個事件，可設定為以 EoS 事件或 BoP 事件執行。第二個封包是等待，會封鎖引擎 (PFP 或 ME)，不允許後續封包通過，同時持續輪詢該記憶體位置，直到輪詢的值滿足比較函式為止。此時，被封鎖的引擎會解除封鎖，工作得以繼續。
* 部分排清
  * 部分排清會讓 CP 等待，直到與頂點、像素或計算著色器 (視封包而定) 相關的所有工作都完成為止。ME 會封鎖所有後續封包，直到收到 SPI 的訊號為止。

將這些概念結合成一個範例：如果我們想要 (在像素著色器上) 讀取先前由計算分派寫入的資源，可以使用任一機制來確保這些作業以正確的順序完成 (注意：這裡只處理管線化，尚未探討快取一致性)。

* 對於柵欄，我們會建立一個在計算工作之後寫入記憶體的 EoS 柵欄 (因此第一個柵欄封包會位於分派之後)。並讓第二個柵欄封包在 ME 引擎上封鎖，直到記憶體中的值更新為止 (我們無法選擇性地封鎖 ME 下游的階段，因此在此情況下，要封鎖的引擎只能選擇 ME 或 PFP)。
* 這也可以透過 CS 部分排清來處理，它會在 ME 上封鎖，直到收到所有計算著色器工作已完成的訊號，才會解除封鎖引擎。部分排清只需要一個封包，而柵欄需要兩個 (因此就記憶體作業而言較便宜)。但柵欄在引擎被封鎖期間需要等待哪些工作方面提供了更大的彈性 (因為記憶體寫入可以發生在特定分派之後，而不是**所有**計算工作之後)。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/GPUSynchronizationScarlett_02.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=d3fd928f33772ccc8832501adbea44ab" alt="Shape Description automatically generated" width="1115" height="320" data-path="images/gdk/features/graphics/GPUSynchronizationScarlett_02.png" />

上圖顯示若未採用任何這些機制，產生者的工作可能會與取用者的工作重疊。|

### XBOX Series X|S 上可用的硬體層級同步處理機制：

了解我們使用柵欄或部分排清 (或 Barrier 等同步處理 API) 時驅動程式所產生的封包很有用，因為每當我們檢視同步處理事件時，這些封包都會顯示在 PIX 擷取中 (每個封包位元遮罩的更多詳細資料可在命令反組譯索引標籤中查看)。

* **ACQUIRE\_MEM**：驅動程式使用此封包來確保表面不再被使用 (使用中的繪製命令)，且與其相關的所有快取都會在後續作業之前排清。驅動程式會將其用於需要同時排空 CB 和 DB (所有 CB/DB 工作完成)，並使下列一或多個快取 (\$K、\$I、L0、L1、L2、LM、CB 快取、DB 快取) 失效，和/或對 L2 進行回寫的作業 ([請點選此連結以查看快取定義](#relevant-caches-on-scarlett))。其運作方式是先等待相關的記憶體位址不再被進行中的工作參照，然後再排清與其相關的快取。它可以在 PFP 或 ME 上封鎖 (此封包在管道頂端執行)。
* **RELEASE\_MEM**：允許在管線上插入事件 (管道結束或著色器結束)。例如，可用來更新柵欄值，或執行記憶體作業 (快取失效/排清)。
* **WAIT\_REG\_MEM**：此封包用於等待暫存器或記憶體空間中的某個位置滿足比較函式。可用來評估柵欄。此封包可定義比較函式 (例如 equal ref 會檢查記憶體中的值是否與參考值相符)，而參考值是我們要比較的值。它也定義哪個引擎要等待 (PFP、ME 或 CE)、定義要比較哪些位元的遮罩，以及輪詢間隔 (引擎檢查記憶體/暫存器空間的頻率，以時脈週期為單位)。
* **EVENT\_WRITE**：允許將事件插入管線。VS、PS 和 CS 部分排清都屬於 EVENT\_WRITE 類型。
* **PFP\_SYNC\_ME**：強制 PFP 與 ME 區塊同步。PFP 會將封包轉送至 ME，並暫停傳入的封包，直到被 ME 解除為止 (ME 已完成工作，且 ME 與 PFP 之間的 FIFO 佇列為空)。
* **DMA\_DATA**：允許 PFP 或 ME 指示 CP DMA 從暫存器或記憶體複製，或複製到暫存器或記憶體 (在一個不可部分完成的封包中)。要求複製的引擎 (PFP 或 ME) 可以選擇性地等待結果。可指定複製為同步或非同步。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/GPUSynchronizationScarlett_03.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=7c948a7e7c91b89da5a3243e36ad236a" alt="A picture" width="1453" height="555" data-path="images/gdk/features/graphics/GPUSynchronizationScarlett_03.png" />

上圖顯示 Barrier 的 PIX 命令反組譯檢視 (此檢視可從 PIX 的版面配置編輯器新增)。它使用 RELEASE\_MEM 封包來執行快取排清與失效 (**CACHE\_FLUSH\_AND\_INV\_TS\_EVENT**)，同時在 EoP 寫入一個 32 位元值。您可以看到它寫入值的位址 (**address\_lo** 和 **address\_hi**)，以及值 (**data\_lo**，0x5)。

<img src="https://mintcdn.com/microsoft-4404708b/EHFikhsC0GyEu9Ca/images/gdk/features/graphics/GPUSynchronizationScarlett_04.png?fit=max&auto=format&n=EHFikhsC0GyEu9Ca&q=85&s=8896b07eef1e5d2d57b465cef185be11" alt="A picture" width="1666" height="510" data-path="images/gdk/features/graphics/GPUSynchronizationScarlett_04.png" />

之後，下一個封包是 WAIT\_REG\_MEM，它會等待前一個封包的記憶體寫入完成。展開它會顯示比較函式類型 (**equal\_ref**)、它是讀取暫存器還是記憶體 (**mem\_space,** 記憶體)、它預期的值 (**reference**，0x5)，以及它要讀取的位址 (**addr\_lo** 和 **addr\_hi**)。請注意，這些位址與 RELEASE\_MEM 封包的位址相符。

因此，此 Barrier 會執行記憶體作業，接著使用柵欄進行封鎖，直到這些作業完成為止。

<a id="cache-coherency" />

## 快取一致性

上述內容只說明了一部分，因為我們仍需確保記憶體存取正確執行。由於 GPU 的工作負載高度平行，其記憶體階層結構比 CPU 複雜得多。它的許多快取並不一致，因此在同步處理方面需要做更多工作。

討論快取時的重要概念：

* 回寫 (WB)：將某個範圍 (或全部) 的快取行寫回主記憶體 (或階層中較上層的快取) 的作業。
* 失效：當快取失效時，其某個範圍 (或全部) 的快取行會標記為無效，未來的讀取會轉送至階層中的下一個快取，以擷取最新的值。
* 直寫：寫入會在快取中進行，同時也轉送至下一層。
* 已變更位元 (Dirty bit)：如果快取行標記為已變更，表示快取已被修改，而主記憶體 (及其他快取) 擁有的是過時的副本。將需要排清 (WB) (並在其他快取上將此資料標記為無效，讓它們可以擷取最新的值)。

命令處理器可以在管道頂端或底部發出快取排清訊號。例如，RELEASE\_MEM 封包可以觸發記憶體作業 (排清)，並接著執行柵欄記憶體寫入。這可確保記憶體作業在柵欄訊號發生之前就緒。AQUIRE\_MEM 也會觸發記憶體作業，但這些作業發生在 ToP。

**以下是 XBOX Series X|S GPU 上快取的圖表**

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

<a id="relevant-caches-on-scarlett" />

### XBOX Series X|S 上的相關快取如下

| **快取名稱** | **大小** | **所在位置** | **描述** | **用戶端對象** |
| - | - | - | - | - |
| L0/TCP/向量快取 | 16KB | 每個 WGP 2 個 (紋理單元) | 第一層快取。由向量記憶體 (VMEM) 指令使用。 | L1 |
| L1 | 128KB | 每個著色器陣列 1 個 | 第二層快取。 | L2 |
| L2 | 5MB (Anaconda) / 2MB (Lockhart) | GPU 中 1 個 | 快取階層的最後一層。 | DRAM |
| \$K (常數) | 16KB | 每個 WGP 1 個 | 唯讀。由純量記憶體 (SMEM) 指令使用。 | L1 |
| \$I (指令) | 32KB | 每個 WGP 1 個 | 著色器指令的唯讀快取。 | L1 |
| LM (中繼資料) | 每個紋理快取通道 (TCC) 8KB | L2 | 中繼資料快取 (唯讀)。保存表面中繼資料。例如在透過著色器讀取壓縮表面時使用。 | L2 |
| CB 快取 (資料/中繼資料) | 約 51KB (對應 CB 內所有快取大小的總和) | 每個著色器陣列 2 個 | 保存色彩資訊與中繼資料 (CMask、DCC、FMASK)。 | L1 |
| DB 快取 (資料/中繼資料) | 約 43KB (對應 DB 內所有快取大小的總和) | 每個著色器陣列 2 個 | 保存深度/樣板資料、ZPLanes 與中繼資料 (HTILE)。 | L1 |

此外，不同的快取在何時回寫/失效方面有不同的策略：

| **快取** | **策略** |
| - | - |
| L0-L1 | 對於寫入，L0 採用直寫原則。寫入會被快取並轉送至階層中的下一個快取。L1 不快取寫入，而是將其轉送至 L2。對於讀取，由於 L0 和 L1 的不一致特性，且我們不追蹤資源寫入歷程記錄，如果先前的存取**可能**是寫入，而下一個是讀取，就必須使 L0 和 L1 失效。如果轉換是從讀取到讀取，則不需要失效。 |
| L2 | L2 的策略是盡可能延後排清，這表示**如果**之後的存取將使用 L2，我們在轉換離開快取時不會回寫寫入。如果先前的存取 (可能) 未使用 L2，而下一個存取會使用，則會使其失效。 |
| CB/DB 快取 | 每當資源停止使用這些區塊 (轉換離開) 時，CB 和 DB 快取會回寫並失效。對於紋理複製，我們不知道複製作業是否會使用 CB/DB，因此需要假設最壞的情況，並在每次存取時將快取標記為已變更。因此，即使沒有轉換離開，快取也會回寫並失效。 |
| \$K | \$K 是唯讀的，因此無法回寫。從未使用快取轉換為使用快取時會失效。 |
| LM | LM 是唯讀的，因此無法回寫。在轉換離開可能已修改基礎中繼資料 (CB/DB) 的狀態時，或之前或之後的存取可能透過它讀取時 (**僅限**資源具有中繼資料的情況)，會失效。 |

回到從 CS 寫入轉換為 PS 讀取的範例，我們現在可以檢視快取。UAV 寫入使用 L0-L1-L2 和 LM 快取。SRV 也使用相同的快取。由於我們是從寫入狀態轉換為讀取狀態，L0 和 L1 會失效。LM 也會失效，因為像素著色器可能會從中讀取。(LM 失效**只會在**資源具有中繼資料時發生)。


## Related topics

- [NDA 主題目錄 - Microsoft Learn 線上連結](/zh-TW/nda/nda-topic-directory.md)
- [Direct3D 12 程式設計指南](/zh-TW/build/gdk-and-engines/guides/directx-12-programming-guide.md)
- [XBOX Series X|S 上的 Sampler Feedback 与 SFS](/zh-CN/build/core-features/graphics/newfeatures/sfs/index.md)
- [D3D12_GLOBAL_BARRIER (XBOX Series 主机)](/zh-CN/reference/graphics/d3d12/structs/d3d12_global_barrier_public.md)
- [XBOX Series X|S 上的网格着色器与 Meshlet](/zh-CN/build/core-features/graphics/newfeatures/ms/index.md)
