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

# 使用 GDKX 呈現 API 的畫面步調與延遲

> 了解使用 GDKX 呈現 API 的畫面步調與延遲

本主題說明 Microsoft Game Development Kit with XBOX Extensions (GDKX) 中用於畫面排程的畫面步調 API，涵蓋這些 API 的目標、功能與陷阱。您將了解如何使用這些 API 在遊戲穩定性與延遲之間取得平衡。

<Note>
  此資訊截至 2022 年 4 月為最新。此後部分功能可能已變更或更新。
</Note>

<a id="frame-pacing-apis" />

## 畫面步調 API

新的畫面步調 API 全面翻新了圖形呈現的流程，其目標如下。

* 對可顯示資源的建立與管理提供更直接的控制
* 降低從輸入裝置取樣到呈現的畫面資源掃描輸出之間的端對端延遲
* 擴充動態設定顯示平面與參數的彈性
* 提高選擇用於提交呈現之命令佇列的自由度
* 建立明確的 CPU 節流機制，以精細控制模擬與轉譯管線的執行
* 強化畫面管線統計資料的維護與報告

這些 API 已推出數年，但在 GDKX 文件中著墨不多。

**圖 1. 包含重大變更的畫面步調 API。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s04.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=f48e299afa07c73f1a7cc5462c341508" alt="畫面步調 API" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s04.png" />

在圖 1 中，程式碼範例有黑色文字與洋紅色文字。過去曾有一項重大變更，自 2019 年 4 月起成為強制性要求。左側黑色的 API 是標準 Direct3D 或 XDK 擴充功能。如果您使用 `Present` API，就會呼叫左側黑色的 API。左側另外兩個是在 ERA 時期新增的擴充功能。右側洋紅色的 API 是新的 API，例如 [PresentX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12commandqueue/methods/id3d12commandqueue_presentx)。

這是一項重大變更，因此移轉至 GDKX 時需要修改程式碼。三年前移轉就已是強制性要求。如果您在過去三年內建立過遊戲，您就已經在使用這些 API。

### 舊方式 - 初始化

**圖 2. 舊方式 - 初始化。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s05.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=640fdfb5885f26f03b846f850959b025" alt="舊方式 - 初始化" width="1280" height="721" data-path="images/gdk/features/graphics/gr03-s05.png" />

圖 2 呈現您先前的 `Present` API 用法。

以往在遊戲開始時的初始化期間，您會建立所謂的「*交換鏈*」。現在已不再以相同的意義使用交換鏈。請注意，交換鏈緩衝區是由系統配置的。

### 舊方式 - 畫面迴圈

**圖 3. 舊方式 - 畫面迴圈。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s06.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=67600773b8f183c86ac4cdb00fa1b615" alt="舊方式 - 畫面迴圈" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s06.png" />

以往對於每個畫面，您會使用下列流程。

1. 向交換鏈要求緩衝區。
2. 將畫面影像轉譯至交換鏈提供的緩衝區。
3. 呼叫 `Present`。

`Present` 是只有少數參數的簡單 API。圖 3 顯示 `Present` 與 `frameInterval` 參數。其值為 1 時代表 60 赫茲 (Hz)。

請注意，這裡沒有時間控制。只要正確，系統就會盡可能地推進。不過，在某些情況下 `Present` 可能會停滯。

### 新方式 - 初始化

**圖 4. 新方式 - 初始化。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s07.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=4902bfd96faf225abefe467e16a9b4f4" alt="新方式 - 初始化" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s07.png" />

圖 4 顯示呈現的初始化。啟動遊戲時，您必須呼叫更多程式碼，因為您的遊戲比以前擁有更多控制權。遊戲會建立自己的交換鏈緩衝區、事先指定畫面播放速率，然後建立畫面原點事件。

如圖 4 所示，您不是建立交換鏈介面，而是像一般資源一樣，一次建立一個自己的交換鏈緩衝區。`HEAP_ALLOW_DISPLAY` 旗標表示您打算將這些緩衝區用於顯示。接著您會呼叫數個新 API。

新的 API [SetFrameIntervalX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_setframeintervalx) 會事先指定您的目標畫面播放速率，而不是等到畫面呈現時才指定。在此案例中，目標畫面播放速率為 60 Hz。若要完成初始化，請排程 `ORIGIN` 事件。此事件有許多用途，包括指定您希望畫面開始的時間。

以上就是初始化時要做的事。至於畫面迴圈，您現在每個畫面要執行下列動作一次。

### 新方式 - 畫面迴圈

**圖 5. 新方式 - 畫面迴圈。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s08.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=703f9fa642281f55ee2a7060580b1f30" alt="新方式 - 畫面迴圈" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s08.png" />

如圖 5 所示，您不是只呼叫 `Present`，而是先呼叫 [WaitFrameEventX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_waitframeeventx)。它會等待您如圖 4 所示排程的 `ORIGIN` 事件。畫面不再能隨時開始，您必須等待。

接著，將畫面轉譯至您自己建立的交換鏈緩衝區。最後，呼叫新版的 `Present`，也就是 `PresentX`。`PresentX` 有許多參數，已不再是簡單的 API。

<a id="motivation-for-new-apis" />

## 新 API 的動機

本節列出舊版 API 遇到的一系列問題，以及新 API 如何解決這些問題。文中提供程式碼範例，圖中以洋紅色文字表示解決方案程式碼。

### 主要主題：延遲

新 API 的主要主題是控制延遲，這是一個重要的概念。如果您常看 Digital Foundry，就會發現他們的評測會試圖精確測量延遲。

透過 XBOX Game Streaming，玩家可以在雲端玩遊戲。延遲對雲端遊戲也很重要。先前的 `Present` API 在延遲方面表現不佳。例如，當您呼叫 `Present` 時，驅動程式會視需要停滯，而且可能停滯很久，您無法控制。

當時的延遲往往是畫面間隔的整數倍。如果您受限於 GPU，即使不需要，通常也會得到最高的延遲。

新 API 具有明確的延遲。遊戲可以要求以微秒為單位的精確延遲，而不是整數個畫面。您可以視需要增加或減少延遲。在高負載期間，您可以使用較高的延遲以避免掉格。在低負載期間，您可以降低延遲，讓遊戲反應更靈敏。

### 問題 1/10：多個顯示平面缺乏擴充性

XBOX One 首次引進第二個顯示平面。它有公開給遊戲使用，但不具擴充性。

### 解決方案：多個顯示平面的擴充性

**圖 6. 多個顯示平面的擴充性。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s12.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=8d880a03231fbb30e1706bfc5aedb852" alt="多個顯示平面的擴充性" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s12.png" />

圖 6 以洋紅色顯示部分硬式編碼，但並非針對兩個顯示平面。現在呼叫 `Present` 時，您可以傳入任意數量的平面，每個平面中可以有任意數量的檢視。此程式碼可處理具有兩個以上平面或每個平面多個顯示的任何硬體。

您可以有一個顯示平面，其中一個檢視是共用指數的 RGBE，第二個檢視則包含 Alpha 通道。也可能有其他情境，例如使用兩個檢視進行分割畫面，或使用兩個檢視進行觀戰模式。這些情境目前不受支援，但 API 已隱含支援它們。

### 問題 2/10：無法分離顯示平面的轉譯速率

此問題與顯示平面擴充性有關。過去無法分離顯示平面的轉譯速率。例如，一個顯示平面以每秒 60 個畫面 (FPS) 轉譯，另一個以 30 FPS 轉譯。硬體支援這種做法，但 API 不支援。

如今對於越來越普遍的 120 Hz 遊戲而言，顯示平面擴充性很重要。UI 不需要以 120 Hz 轉譯。如果您以 120 Hz 執行，每個畫面只有很短的時間。

### 解決方案：分離顯示平面轉譯速率的方法

**圖 7. 分離顯示平面的轉譯速率。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s14.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=7f932d3cd6560df2e1eb681040c8b746" alt="分離顯示平面的轉譯速率" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s14.png" />

如圖 7 所示，新 API 隱含支援分離轉譯速率，因為您會傳入要顯示的資源。因此，您可以連續兩次為 UI 平面傳入相同的資源。

### 問題 3/10：沒有 PresentationThreshold 來支援有限度的撕裂

下一個問題是 XBOX 360 時代的 `PresentationThreshold`。這是一種在畫面撕裂與掉格之間取捨的方法。

`PresentationThreshold` 是透過 XBOX 360 上的擴充功能公開的。它是 0 到 100 的撕裂百分比。0% 是預設值，表示永不撕裂。50% 表示如果錯過畫面預算，而畫面完成時只掃描了畫面上方 50%，就會立即使用 `Present` 並產生撕裂。

如果已使用超過 50%，就不需要撕裂，您會等待下一個 Vblank。另一個極端是 100%，表示只要達到畫面播放速率就不撕裂。不過，若錯過畫面目標，就可能發生撕裂。

### 解決方案：使用 PresentationThreshold 支援有限度的撕裂

**圖 8. 使用 PresentationThreshold 支援有限度的撕裂。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s16.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=0124ac9ca03d2d37cb54ef05291a620b" alt="解決方案：使用 PresentationThreshold 支援有限度的撕裂" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s16.png" />

圖 8 顯示新 API 中 [ImmediateThresholdPercent](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_present_parameters) 的用法。

`PresentationThreshold` 支援有限度的撕裂。您現在就可以使用。許多遊戲使用 100% 模式，這可能是最常見的用法。

### 問題 4/10：不支援高於 60 FPS 的畫面播放速率

先前的 API 不支援高於 60 FPS 的畫面播放速率。由於畫面間隔是 UINT，因此無法表達。1 代表 60 FPS。您無法使用 1 的一半，因為一半不是整數。

先前的 API 無法表示您想要 120 Hz，因此需要新的 API。

新 API 支援 120 Hz，並隱含支援該畫面時間的任何倍數。藉此您可以得到 40 FPS，或最近新增的過場動畫用 24 FPS。

### 解決方案：支援高於 60 FPS 的畫面播放速率

**圖 9. 支援高於 60 FPS 的畫面播放速率。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s18.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=aee0bebcd833877df7800de7f3638cdf" alt="支援高於 60 FPS 的畫面播放速率" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s18.png" />

如圖 9 所示，解決方案是使用 [LengthInMicroseconds](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_setframeintervalx) 參數。

它仍是不帶正負號的整數，但現在以微秒為單位。任何畫面播放速率都可以在此表達。傳入您想要的微秒長度，如果該長度受支援，API 就會成功。

如果未來某個時候需要支援 240 FPS，API 不需要變更。最近新增 24 FPS 支援時，API 也沒有變更。

<Note>
  偵測 120 FPS 支援的正確方式是使用 [GetDisplayModeListX](/zh-TW/reference/graphics/d3d12_xs/interfaces/IDXGIOutput/methods/idxgioutput_getdisplaymodelist)。它不會呼叫 [SetFrameIntervalX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_setframeintervalx) 或檢查傳回碼。
</Note>

### 問題 5/10：無法在計算佇列上完成畫面

先前的 API 呼叫 `Present`，它是交換鏈介面的成員。交換鏈隱含屬於圖形佇列。

畫面的最後工作完全以計算為主是很常見的。如果能執行該工作、呼叫 `Present`，然後已經開始下一個畫面的圖形工作，會很有幫助。圖形與計算會稍微重疊，管線化不足。

先前曾有範例示範如何使用舊 API 在計算佇列上完成畫面。不過，由於這項缺漏，做法相當複雜。

### 解決方案：在計算佇列上完成畫面的方法

**圖 10. 在計算佇列上完成畫面的方法。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s20.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=a50983f591390971740b4a30e1931a3d" alt="在計算佇列上完成畫面的方法" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s20.png" />

圖 10 顯示新 API 中已不再有交換鏈介面。`Present` 已移至命令佇列，因此 API 可以表示畫面可在任何命令佇列上完成。`PresentX` 是 [ID3D12CommandQueue](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12commandqueue/id3d12commandqueue) (基底類別) 的方法，而不是已不存在的 [IDXGISwapChain](/zh-TW/reference/graphics/d3d12_xs/interfaces/IDXGISwapChain/idxgiswapchain) 的方法。

目前圖形與非同步計算都支援此功能。如果未來有理由在新類型的佇列或現有佇列 (例如直接記憶體存取 (DMA)) 上支援它，也可以在不變更 API 的情況下完成。

### 問題 6/10：節流缺乏精細控制

先前的 API 無法精細控制節流。節流是刻意的停滯，用來防止延遲無限制地增加。這是轉譯延遲，預設通常是畫面間隔的整數倍。

遊戲的變通方式是新增第三個交換鏈緩衝區、在畫面中途某處發出柵欄訊號，然後在 CPU 上等待該柵欄。基本上，遊戲會在驅動程式自動節流之前手動節流。不過，這並不精確。您可以節流一個畫面加上一次深度預先處理，但無法節流一個畫面加 4 毫秒。

### 解決方案：精細控制節流

**圖 11. 精細控制節流。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s22.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=ed588b40dc676cb828e5583e994ddfdc" alt="精細控制節流" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s22.png" />

圖 11 顯示新 API 的控制方式。此控制是透過洋紅色文字中的兩個參數完成 (本主題稍後會更詳細說明)。第一個參數是 [PeriodInIntervals](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_setframeintervalx)，表示從開始到結束完成一個畫面需要多少個畫面播放速率間隔。

例如，如果畫面播放速率為 60 FPS，畫面播放速率間隔就是 16.7 毫秒。從畫面開始到結束可能需要多個間隔。

第二個參數是 [IntervalOffsetInMicroseconds](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_scheduleframeeventx)。請將其用於小數畫面的延遲。也許以間隔計的週期是 2，但實際上需要介於 1 與 2 之間。可以找到適當的間隔位移值來達成此目的。

### 問題 7/10：控制器輸入沒有明確同步

先前的 API 不允許您與控制器同步。這是造成延遲的重要因素，對無線控制器而言尤其如此。按下按鈕時，遊戲不會立即反應。

為了節省電池壽命，無線控制器只會每隔一段時間傳送更新。玩家可能在更新前一刻按下按鈕，並立即得到該更新。不過，如果玩家在更新後一刻按下按鈕，就必須等待下一次更新，按鈕按下才會被辨識。這就是延遲。從某種角度來說，這是最糟的延遲，因為每個畫面的延遲都不同。

如果玩家知道延遲會是多少，而且延遲總是相同，他們就能稍微補償。否則，在講求反應速度的遊戲中，就很難在需要的那一刻精準按下按鈕。

### 解決方案：與控制器輸入明確同步

**圖 12. 與控制器輸入明確同步。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s24.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=074ff997daae186e9eb17fa2b963891c" alt="與控制器輸入明確同步" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s24.png" />

圖 12 顯示如果您以可預測的方式排程 ORIGIN 事件，它能發揮的用途之一。無線控制器可以及時傳送更新，也許在遊戲需要之前 1 毫秒。這會降低延遲，而且每個畫面的延遲都相同。

### 問題 8/10：重要事件沒有通知

除了 Vblank 之外，先前的 API 不允許重要事件的通知。

遊戲知道 Vblank 中斷何時發生很重要。部分此功能已被移除。不過，仍有方法可以取得重要事件的通知。

### 解決方案：重要事件的通知

**圖 13. 重要事件的通知。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s26.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=b389e24786854977ecbbc5fc0ad85f8c" alt="重要事件的通知" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s26.png" />

如圖 13 所示，新 API 預設支援兩個事件：Vblank 通知與 Flip 通知。Flip 事件會在新的交換鏈緩衝區變為可見時發生，而且具擴充性。Flip 通知是得知何時可以安全地再次循環使用交換緩衝區的便利方式。

可以在不變更 API 的情況下為 Flip 新增更多內容。

### 問題 9/10：PIX 無法顯示畫面

過去，PIX 計時擷取無法顯示 (追蹤) 整個畫面。PIX 對畫面僅有的資訊，是您使用舊 API 呼叫 `Present` 的時間。沒有任何關於哪些工作促成該 `Present` 呼叫，或該畫面何時開始的資訊。

### 解決方案：讓 PIX 顯示畫面的方法

**圖 14. 讓 PIX 顯示畫面的方法。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s28.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=ca1d74d69b02c2b92807c7d17ba323dd" alt="讓 PIX 顯示畫面的方法" width="1280" height="721" data-path="images/gdk/features/graphics/gr03-s28.png" />

圖 14 顯示畫面管線權杖。當您等待 ORIGIN 事件時，會收到畫面管線權杖作為傳回值。請儲存它，因為稍後會用到。呼叫 `Present` 時，您會傳入相同的權杖。PIX 現在可以辨識：

* 畫面的開始，以及該畫面對應的結束。
* 當您傳入相同權杖時，其他事件屬於同一個畫面。

### 問題 10/10：遊戲無法擷取詳細的計時資料

先前的 API 無法擷取詳細的計時資料。對您的遊戲而言，您可能想知道某些作業花了多久或何時發生。例如：

* GPU 在一個畫面中忙碌了多少時間？
* 畫面何時開始？
* ORIGIN 事件何時發生？
* 畫面何時呈現？
* `Present` 呼叫何時抵達 GPU？

遊戲可以使用查詢自行解決部分問題。不過，如果驅動程式能在同一處提供所有這些資訊會更好。

### 解決方案：讓遊戲擷取詳細計時資料的方法

**圖 15. 讓遊戲擷取詳細計時資料的方法。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s30.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=f40361892457ef1ab56540df65b21078" alt="讓遊戲擷取詳細計時資料的方法" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s30.png" />

圖 15 顯示新 API 使用 [GetFrameStatisticsX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_getframestatisticsx) 擷取詳細的計時資料。它提供許多統計資料，且與其他 API 一樣具擴充性。可以在不變更 API 簽章的情況下新增新類型的統計資料。

<a id="recommended-usage-of-frame-pacing-apis" />

## 畫面步調 API 的建議用法

本節說明如何調節延遲：找出您目前的延遲、決定您想要的延遲，然後使用畫面步調 API 進行變更。

### 調節延遲

**圖 16. 調節延遲：三個簡單步驟。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s32.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=4075553b4fbed29d82c0b3a4210d629f" alt="調節延遲：三個簡單步驟" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s32.png" />

圖 16 顯示調節延遲的三個建議步驟。

1. 使用畫面統計資料測量遊戲中的理想延遲。
2. 為變動性加上一些餘裕。
3. 計算參數。

在第一個步驟中，有兩個重要的統計資料：發出畫面開始訊號的 ORIGIN [SignalTime](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_frame_event_statistics)，以及在呼叫 \`Present' 並抵達 GPU 時發出畫面結束訊號的 [GPUProcessTime](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_present_statistics)。此時您要顯示的畫面緩衝區已完成，兩個統計資料之間的差值就是遊戲的理想延遲。

在第二個步驟中，最好保留餘裕，以應對理想延遲不完全準確的情況。如果您的餘裕估計過低，而下一次延遲較高，就會掉格。如果估計過高，延遲就會高於所需。

第三個步驟是使用所有這些資訊計算兩個參數：`PeriodInIntervals` 與 `IntervalOffsetInMicroseconds`。必須將它們提供給驅動程式。

這些變更是管線化的，因此會在幾個畫面內生效。您可能希望遊戲對延遲上升立即反應，但對延遲下降則緩慢反應。

### 調節延遲範例

**圖 17. 調節延遲範例。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s33.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=54445dcbf94fed7eeafa8f7c25b740aa" alt="調節延遲範例" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s33.png" />

圖 17 顯示調節延遲的範例。

此範例假設遊戲為 60 Hz，畫面間隔為 16.7 毫秒。根據 `SignalTime` 與 `GPUProcessTime` 之間的差值，測得的延遲為 30 毫秒。例如，您決定安全餘裕為半個畫面，即 8.3 毫秒。這樣要求的總延遲就是 38.3 毫秒。

若要計算 `PeriodInIntervals` 與 `IntervalOffsetInMicroseconds` 這兩個參數，請參閱圖 17 中的圖表。圖表右側是目標 Vblank，也就是畫面必須準備好進行 `Present` 的時間。從該點往回推算 8.3 毫秒的安全餘裕，就得到預測畫面完成的時間。從那裡再往回推算測得的 30 毫秒延遲，就是您的 ORIGIN 點。

圖 17 將 38.3 毫秒 (38300) 除以 16.7 毫秒 (16667) 的畫面間隔，計算出以間隔計的畫面週期 `PeriodInIntervals`。結果無條件進位為三個間隔。

`IntervalOffsetInMicroseconds` 是畫面在第一個間隔中開始的位置。圖 17 中的計算顯示它是 11.7 毫秒。

總結來說，這表示目標 Vblank 是 ORIGIN 點之後的第三個 Vblank，而 ORIGIN 點位於畫面間隔中途 (11.7 毫秒)。

此流程並不太複雜，但有可能變得複雜。在同時處理其他事情的遊戲中，可能就會如此。

如果您的遊戲具有動態解析度，就必須將其納入結果考量。如果延遲非常高，您可以透過降低解析度來處理。有些遊戲會動態調節畫面播放速率，表示有時是 60 赫茲遊戲，有時是 120 赫茲遊戲。其他遊戲則在超過畫面預算時使用 `ImmediateThreshold` 進行撕裂。

您可以建立非常複雜的演算法來預測所有可能的情況。

<a id="nonrecommended-usage-of-frame-pacing-apis" />

## 畫面步調 API 的不建議用法

本節說明我們不建議的畫面步調 API 用法。

### 背景緩衝區未在約定時間內呈現

本主題到目前為止涵蓋的問題類似於一份合約。您同意畫面會在特定時間內完成。如果您未在約定時間內完成畫面，就可能發生掉格等問題。以往只有在 CPU 或 GPU 超出預算時才會掉格。現在，您可能做出延遲預測卻未能達成，即使 CPU 與 GPU 都在時間預算內，仍會掉格。

### 遊戲使用的交換鏈太短

當遊戲使用的交換鏈太短時，可能會發生停滯等問題。理想的交換鏈大小是 `PeriodInIntervals` 加一。例如，如果您使用的 `PeriodInIntervals` 等於 2，且 `IntervalOffsetInMicroseconds` 為 0，就需要大小為 3 的交換鏈。這是因為您有一個正在顯示的畫面、另一個正處於計算第二個間隔的畫面，以及第三個正處於計算第一個間隔的畫面。總共三個畫面同時存在。嚴格來說，它們需要不同的交換鏈緩衝區，否則可能會發生衝突。

在某些情況下，交換鏈太短不會造成任何損失，什麼事也不會發生。不過，在其他情況下，CPU 會在 `PresentX` 中停滯，或 GPU 會在 [ResourceBarrier](/zh-TW/reference/graphics/d3d12/interfaces/id3d12graphicscommandlist/methods/id3d12graphicscommandlist_resourcebarrier_public) 中停滯。有時停滯不會影響畫面播放速率，有時則會。

您可以為上述每種情境建立範例。您選擇哪一種，取決於遊戲的運作方式。

### 遊戲只在啟動時設定一次 PeriodInIntervals 與 IntervalOffsetInMicroseconds

如果遊戲在啟動時設定新參數 `PeriodInIntervals` 與 `IntervalOffsetInMicroseconds`，之後便不再處理它們，就可能發生問題。這類似於以前的做法：事先設定目標延遲，然後無論遊戲中發生什麼事都維持相同的選擇。

如果您的延遲選擇過於積極或過於保守，就可能產生損失。過於積極，即使在預算內也會不必要地掉格。過於保守，延遲總是高於所需。

### 遊戲在 ORIGIN 事件發出訊號前就開始計算畫面

當您排程 ORIGIN 事件並等待它，但遊戲已開始畫面計算時，就會發生此問題。在此情況下，計算並未在您設定的時間開始，因此延遲會來回波動而變得無法確定。這會導致控制器輸入無法同步，也無法在 PIX 中追蹤畫面。

<Note>
  在本主題提出的問題中，這是最重要的一項。目前尚不清楚為何在您排程 ORIGIN 事件並等待它時，這只有在某些時候才有效。
</Note>

<a id="demos" />

## 示範

### PIX System Monitor 中的畫面步調

**圖 19. PIX System Monitor 中的畫面步調。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s41.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=9a8807e77e57da3e65e9511019bfeee6" alt="PIX System Monitor 中的畫面步調" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s41.png" />

圖 19 標示 PIX System Monitor 的 D3D (Direct3D) 區段。D3D 區段會追蹤對計算端對端畫面延遲很重要的事件。

示範說明下列元素。

* **Frames per Second**：顧名思義。
* **Total Frame Latency**：從 ORIGIN 事件到畫面翻轉並成為前景緩衝區的時間。
* **Frame Origin**：ORIGIN 事件在 Vblank 之後多久發生 (IntervalOffsetInMicroseconds)？
* **Present to Flip**：畫面在翻轉之前多早就已就緒 (`PresentX` 抵達 GPU)？
* **Wait for Signal**：`WaitFrameEventX(ORIGIN)` 封鎖了多久？

### Frame Pacing Sample

**圖 20. Frame Pacing Sample UI。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s43.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=3d10e8701fd9cd6f953b25089bcfe56d" alt="Frame Pacing Sample" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s43.png" />

圖 20 顯示 Frame Pacing Sample 的 UI。此範例尚未公開發佈。此範例會在 CPU 與 GPU 上人為產生畫面時間，然後在 UI 中顯示結果。

UI 分為下列部分。

* **功能表**：
  * 位於右上角。
  * 分為兩個區段：
    * 畫面負載參數。
    * 畫面步調參數。
  * 畫面負載由計時器強制執行。
* **圖表**：
  * 位於左上角的三個折線圖。
    * 畫面時間/播放速率。
    * 理想延遲。
    * 延遲餘裕 (安全餘裕)。
  * 負值餘裕表示畫面延遲。
* **時間軸**：
  * 類似簡化的 PIX 計時擷取。
  * 顯示下列資訊。
    * CPU 更新。
    * CPU 轉譯。
    * GPU 圖形佇列。
    * GPU 計算佇列。
    * Flip。
  * Flip 會告訴您正在顯示哪一個交換鏈緩衝區。
  * 時間軸圖表依交換鏈緩衝區以色彩區分。
  * 圖 20 的範例有三個交換鏈緩衝區，以紫色、綠色與黃色表示。

### 小數畫面的延遲

先前的 API 不允許小數畫面的延遲。不過，新 API 允許，讓遊戲能以微秒為單位指定小數位移。

**圖 21. 小數畫面的延遲 1/2。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s45.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=30fbea888b9cc8e48cff3ab9299d0e04" alt="小數畫面的延遲 1/2" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s45.png" />

圖 21 顯示沒有小數畫面延遲的範例。請注意 **Frame Offset (ms)** 為 0。CPU 更新 (ORIGIN 事件) 在 Vblank 時發生。這表示延遲是整數個畫面。

**圖 22. 小數畫面的延遲 2/2。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s46.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=db8ab8b1ee16422d7d578f517f6ff7dd" alt="小數畫面的延遲 2/2" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s46.png" />

圖 22 顯示 **Frame Offset (ms)** 設定為 3 的範例。這是不掉格情況下可設定的最高位移。這會使延遲餘裕接近 0，低得危險。請注意，CPU 更新開始的 ORIGIN 事件已不再與 Flip 結束的 Vblank 同步。

### 延遲與交換鏈大小脫鉤

**圖 23. 延遲與交換鏈大小脫鉤 1/2。**

使用新 API 時，延遲不再取決於交換鏈長度。此示範顯示下列情況會發生什麼事：

* 交換鏈大小為我們建議的大小。
* 交換鏈大小小於我們建議的大小。

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s48.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=d4068c216377eac37e1e90d1d329cb72" alt="延遲與交換鏈大小脫鉤 1/2" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s48.png" />

圖 23 顯示 **Frame Period** 設定為 2 的範例。這通常需要三個畫面緩衝區。請注意底部的計時列中有三種色彩：紫色、綠色與黃色。每種色彩代表一個交換鏈緩衝區。

**圖 24. 延遲與交換鏈大小脫鉤 2/2。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s49.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=5a90cf514e94ee2cf828783fc9fd06e3" alt="延遲與交換鏈大小脫鉤 2/2" width="1280" height="719" data-path="images/gdk/features/graphics/gr03-s49.png" />

圖 24 顯示畫面緩衝區變更為 2 的範例。請注意底部的計時列中現在有兩種色彩：綠色與黃色。另請注意，它看起來與圖 23 類似。在圖 24 中，折線圖中的理想延遲為 30.2，延遲餘裕為 3.1。

如果新增另一個畫面緩衝區 (圖 23)，則沒有任何變化，畫面延遲維持不變。額外的畫面緩衝區不會增加延遲，但會產生額外的記憶體成本。

此示範也顯示，即使畫面 N 的綠色 Flip 與畫面 N+2 的綠色列重疊，畫面播放速率也沒有改變。畫面播放速率穩定維持在 59.0 FPS。

仍可能發生停滯。例如，如果緩衝區在傳遞給 `Present` 時仍在顯示中，CPU 上的 [BlockOnFence\_SwapThrottle](/zh-TW/tools/tools-console/pix/new-timing-captures) 可能會停滯。如果緩衝區在從 PRESENT 狀態轉換為可寫入狀態時仍在顯示中，GPU 上的 [WaitOnSwapChainSurface](/zh-TW/tools/tools-console/pix/new-timing-captures) 也可能停滯。這些作業能延後得越久，所需的緩衝就越少。

您可以在 Frame Pacing Sample 中縮短 **CPU Update** 與 **CPU Render** 的時間，使這些停滯發生。

### 延遲調節

**圖 25. 延遲調節 1/4。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s51.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=95ef4692da708b353ac26c0aed1ece04" alt="延遲調節 1/4" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s51.png" />

圖 25 顯示 **CPU Update** 增加至 14 毫秒的範例。請注意延遲餘裕為負值。遊戲錯過了畫面期限，導致掉格。根據功能表的 **Frame load**，沒有任何處理步驟的成本低於一個畫面間隔 (16.7 毫秒)。這就是延遲預測過於積極時會發生的情況。

**圖 26. 延遲調節 2/4。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s52.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=40d9f343ee4995b343dbc1d17156119d" alt="延遲調節 2/4" width="1280" height="721" data-path="images/gdk/features/graphics/gr03-s52.png" />

圖 26 顯示已啟動自動畫面步調模式的範例。它將 **Frame Period** 從 2 增加為 3，修正了掉格問題。請注意 **Frame Offset** 從 0.00 變更為 7.45 毫秒，而 **Legacy margin** 現在顯示 8.3 毫秒。

**圖 27. 延遲調節 3/4。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s54.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=7e481f3e016ce363015a37de2302b09b" alt="延遲調節 3/4" width="1280" height="723" data-path="images/gdk/features/graphics/gr03-s54.png" />

圖 27 顯示已啟用 **Auto Frame Load**，以及每秒有一個畫面超出預算的情境。此「*GPU 異常*」會導致下一個畫面掉格。這會顯示在 **Frame time** 圖表與時間軸的間隙中。每秒發生一次異常，是因為延遲餘裕太低。

**圖 28. 延遲調節 4/4。**

<img src="https://mintcdn.com/microsoft-4404708b/6hkHc7s12mHcTMCE/images/gdk/features/graphics/gr03-s55.png?fit=max&auto=format&n=6hkHc7s12mHcTMCE&q=85&s=2b33a0cf473121ac53ed3cc39a6698b8" alt="延遲調節 4/4" width="1280" height="720" data-path="images/gdk/features/graphics/gr03-s55.png" />

圖 28 顯示相同情境下再次開啟 **Auto Frame Pacing** 模式。演算法透過降低延遲預測的積極程度來修正此問題。延遲餘裕增加至 8.3 毫秒，避免了掉格。延遲餘裕圖表不會降到 0 以下。

<a id="next-steps" />

## 後續步驟

如果您想檢閱與本概觀相關的簡報資料，請從 [XBOX Developer Download Secure Data Storage (SDS)](https://www.microsoft.com/en-us/software-download/gdk) 下載下列檔案。

* 投影片：Xfest 2022 Graphics Track PowerPoints (Xfest2022GfxPPTs.ZIP) 中的 GR03\_Understanding Frame Pacing and Latency with the GDKX Presentation APIs.pptx
* 影片簡報：Xfest 2022 Graphics Track Videos (Xfest2022GfxVids.ZIP) 中的 GR03.mp4

### 下載 Microsoft PowerPoint 簡報與影片

1. 前往 [Game Development Kit (GDK) 下載](https://www.microsoft.com/en-us/software-download/gdk)。
2. 在 <b>Select file type</b> 下，選取 **Conference Materials**，然後選取 **Confirm**。
3. 在 <b>Select build/version number</b> 下，選擇下列其中一個圖形簡報。
   * Xfest 2022 Graphics Track PowerPoints
   * Xfest 2022 Graphics Track Videos
4. 選取 <b>Download Now</b>，然後等待 .zip 檔案下載完成。
5. 前往本機的 \[下載] 資料夾，然後從壓縮資料夾中解壓縮 (擷取) 檔案。

<a id="gdk-graphics-samples" />

### 下載本文提及的 Microsoft Game Development Kit 主機範例

1. 前往 [Game Development Kit (GDK) 下載](https://www.microsoft.com/en-us/software-download/gdk)。
2. 在 <b>Select file type</b> 下，選取 **GDK**，然後選取 **Confirm**。
3. 在 <b>Select build/version number</b> 下，選擇最新的 **Microsoft GDKX (Sample only)** 選項。
4. 選取 <b>Download Now</b>，然後等待 .zip 檔案下載完成。
5. 前往本機的 \[下載] 資料夾，然後從壓縮資料夾中解壓縮 (擷取) 檔案。
6. 選取 **GDKSamples** > **Graphics**，然後搜尋該範例。

<a id="see-also" />

## 另請參閱

[遊戲串流簡介](/zh-TW/build/core-features/common/game-streaming/game-streaming-overview)

[遊戲串流延遲補償概觀](/zh-TW/build/core-features/common/game-streaming/game-streaming-latency-compensation-overview)

[深入探討遊戲串流延遲補償](/zh-TW/build/core-features/common/game-streaming/game-streaming-latency-compensation-deep-dive)


## Related topics

- [NDA 主題目錄 - Microsoft Learn 線上連結](/zh-TW/nda/nda-topic-directory.md)
- [使用 GDKX 建置並執行你的第一個主機遊戲](/zh-TW/home/build-first-title/first-console-title-walkthrough.md)
- [認證主機 BVT 指南](/zh-TW/publishing/game-publishing/concepts/certification/certification-console-bvt-guide.md)
- [最常失敗的測試案例](/zh-TW/publishing/certification/xr-top-failing.md)
- [XBOX 主機遊戲的認證測試 XBOX 需求](/zh-TW/publishing/certification/console-cert-tested-requirements.md)
