此資訊截至 2022 年 4 月為最新。此後部分功能可能已變更或更新。
畫面步調 API
新的畫面步調 API 全面翻新了圖形呈現的流程,其目標如下。- 對可顯示資源的建立與管理提供更直接的控制
- 降低從輸入裝置取樣到呈現的畫面資源掃描輸出之間的端對端延遲
- 擴充動態設定顯示平面與參數的彈性
- 提高選擇用於提交呈現之命令佇列的自由度
- 建立明確的 CPU 節流機制,以精細控制模擬與轉譯管線的執行
- 強化畫面管線統計資料的維護與報告

Present API,就會呼叫左側黑色的 API。左側另外兩個是在 ERA 時期新增的擴充功能。右側洋紅色的 API 是新的 API,例如 PresentX。
這是一項重大變更,因此移轉至 GDKX 時需要修改程式碼。三年前移轉就已是強制性要求。如果您在過去三年內建立過遊戲,您就已經在使用這些 API。
舊方式 - 初始化
圖 2. 舊方式 - 初始化。
Present API 用法。
以往在遊戲開始時的初始化期間,您會建立所謂的「交換鏈」。現在已不再以相同的意義使用交換鏈。請注意,交換鏈緩衝區是由系統配置的。
舊方式 - 畫面迴圈
圖 3. 舊方式 - 畫面迴圈。
- 向交換鏈要求緩衝區。
- 將畫面影像轉譯至交換鏈提供的緩衝區。
- 呼叫
Present。
Present 是只有少數參數的簡單 API。圖 3 顯示 Present 與 frameInterval 參數。其值為 1 時代表 60 赫茲 (Hz)。
請注意,這裡沒有時間控制。只要正確,系統就會盡可能地推進。不過,在某些情況下 Present 可能會停滯。
新方式 - 初始化
圖 4. 新方式 - 初始化。
HEAP_ALLOW_DISPLAY 旗標表示您打算將這些緩衝區用於顯示。接著您會呼叫數個新 API。
新的 API SetFrameIntervalX 會事先指定您的目標畫面播放速率,而不是等到畫面呈現時才指定。在此案例中,目標畫面播放速率為 60 Hz。若要完成初始化,請排程 ORIGIN 事件。此事件有許多用途,包括指定您希望畫面開始的時間。
以上就是初始化時要做的事。至於畫面迴圈,您現在每個畫面要執行下列動作一次。
新方式 - 畫面迴圈
圖 5. 新方式 - 畫面迴圈。
Present,而是先呼叫 WaitFrameEventX。它會等待您如圖 4 所示排程的 ORIGIN 事件。畫面不再能隨時開始,您必須等待。
接著,將畫面轉譯至您自己建立的交換鏈緩衝區。最後,呼叫新版的 Present,也就是 PresentX。PresentX 有許多參數,已不再是簡單的 API。
新 API 的動機
本節列出舊版 API 遇到的一系列問題,以及新 API 如何解決這些問題。文中提供程式碼範例,圖中以洋紅色文字表示解決方案程式碼。主要主題:延遲
新 API 的主要主題是控制延遲,這是一個重要的概念。如果您常看 Digital Foundry,就會發現他們的評測會試圖精確測量延遲。 透過 XBOX Game Streaming,玩家可以在雲端玩遊戲。延遲對雲端遊戲也很重要。先前的Present API 在延遲方面表現不佳。例如,當您呼叫 Present 時,驅動程式會視需要停滯,而且可能停滯很久,您無法控制。
當時的延遲往往是畫面間隔的整數倍。如果您受限於 GPU,即使不需要,通常也會得到最高的延遲。
新 API 具有明確的延遲。遊戲可以要求以微秒為單位的精確延遲,而不是整數個畫面。您可以視需要增加或減少延遲。在高負載期間,您可以使用較高的延遲以避免掉格。在低負載期間,您可以降低延遲,讓遊戲反應更靈敏。
問題 1/10:多個顯示平面缺乏擴充性
XBOX One 首次引進第二個顯示平面。它有公開給遊戲使用,但不具擴充性。解決方案:多個顯示平面的擴充性
圖 6. 多個顯示平面的擴充性。
Present 時,您可以傳入任意數量的平面,每個平面中可以有任意數量的檢視。此程式碼可處理具有兩個以上平面或每個平面多個顯示的任何硬體。
您可以有一個顯示平面,其中一個檢視是共用指數的 RGBE,第二個檢視則包含 Alpha 通道。也可能有其他情境,例如使用兩個檢視進行分割畫面,或使用兩個檢視進行觀戰模式。這些情境目前不受支援,但 API 已隱含支援它們。
問題 2/10:無法分離顯示平面的轉譯速率
此問題與顯示平面擴充性有關。過去無法分離顯示平面的轉譯速率。例如,一個顯示平面以每秒 60 個畫面 (FPS) 轉譯,另一個以 30 FPS 轉譯。硬體支援這種做法,但 API 不支援。 如今對於越來越普遍的 120 Hz 遊戲而言,顯示平面擴充性很重要。UI 不需要以 120 Hz 轉譯。如果您以 120 Hz 執行,每個畫面只有很短的時間。解決方案:分離顯示平面轉譯速率的方法
圖 7. 分離顯示平面的轉譯速率。
問題 3/10:沒有 PresentationThreshold 來支援有限度的撕裂
下一個問題是 XBOX 360 時代的PresentationThreshold。這是一種在畫面撕裂與掉格之間取捨的方法。
PresentationThreshold 是透過 XBOX 360 上的擴充功能公開的。它是 0 到 100 的撕裂百分比。0% 是預設值,表示永不撕裂。50% 表示如果錯過畫面預算,而畫面完成時只掃描了畫面上方 50%,就會立即使用 Present 並產生撕裂。
如果已使用超過 50%,就不需要撕裂,您會等待下一個 Vblank。另一個極端是 100%,表示只要達到畫面播放速率就不撕裂。不過,若錯過畫面目標,就可能發生撕裂。
解決方案:使用 PresentationThreshold 支援有限度的撕裂
圖 8. 使用 PresentationThreshold 支援有限度的撕裂。
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 的畫面播放速率。
偵測 120 FPS 支援的正確方式是使用 GetDisplayModeListX。它不會呼叫 SetFrameIntervalX 或檢查傳回碼。
問題 5/10:無法在計算佇列上完成畫面
先前的 API 呼叫Present,它是交換鏈介面的成員。交換鏈隱含屬於圖形佇列。
畫面的最後工作完全以計算為主是很常見的。如果能執行該工作、呼叫 Present,然後已經開始下一個畫面的圖形工作,會很有幫助。圖形與計算會稍微重疊,管線化不足。
先前曾有範例示範如何使用舊 API 在計算佇列上完成畫面。不過,由於這項缺漏,做法相當複雜。
解決方案:在計算佇列上完成畫面的方法
圖 10. 在計算佇列上完成畫面的方法。
Present 已移至命令佇列,因此 API 可以表示畫面可在任何命令佇列上完成。PresentX 是 ID3D12CommandQueue (基底類別) 的方法,而不是已不存在的 IDXGISwapChain 的方法。
目前圖形與非同步計算都支援此功能。如果未來有理由在新類型的佇列或現有佇列 (例如直接記憶體存取 (DMA)) 上支援它,也可以在不變更 API 的情況下完成。
問題 6/10:節流缺乏精細控制
先前的 API 無法精細控制節流。節流是刻意的停滯,用來防止延遲無限制地增加。這是轉譯延遲,預設通常是畫面間隔的整數倍。 遊戲的變通方式是新增第三個交換鏈緩衝區、在畫面中途某處發出柵欄訊號,然後在 CPU 上等待該柵欄。基本上,遊戲會在驅動程式自動節流之前手動節流。不過,這並不精確。您可以節流一個畫面加上一次深度預先處理,但無法節流一個畫面加 4 毫秒。解決方案:精細控制節流
圖 11. 精細控制節流。
問題 7/10:控制器輸入沒有明確同步
先前的 API 不允許您與控制器同步。這是造成延遲的重要因素,對無線控制器而言尤其如此。按下按鈕時,遊戲不會立即反應。 為了節省電池壽命,無線控制器只會每隔一段時間傳送更新。玩家可能在更新前一刻按下按鈕,並立即得到該更新。不過,如果玩家在更新後一刻按下按鈕,就必須等待下一次更新,按鈕按下才會被辨識。這就是延遲。從某種角度來說,這是最糟的延遲,因為每個畫面的延遲都不同。 如果玩家知道延遲會是多少,而且延遲總是相同,他們就能稍微補償。否則,在講求反應速度的遊戲中,就很難在需要的那一刻精準按下按鈕。解決方案:與控制器輸入明確同步
圖 12. 與控制器輸入明確同步。
問題 8/10:重要事件沒有通知
除了 Vblank 之外,先前的 API 不允許重要事件的通知。 遊戲知道 Vblank 中斷何時發生很重要。部分此功能已被移除。不過,仍有方法可以取得重要事件的通知。解決方案:重要事件的通知
圖 13. 重要事件的通知。
問題 9/10:PIX 無法顯示畫面
過去,PIX 計時擷取無法顯示 (追蹤) 整個畫面。PIX 對畫面僅有的資訊,是您使用舊 API 呼叫Present 的時間。沒有任何關於哪些工作促成該 Present 呼叫,或該畫面何時開始的資訊。
解決方案:讓 PIX 顯示畫面的方法
圖 14. 讓 PIX 顯示畫面的方法。
Present 時,您會傳入相同的權杖。PIX 現在可以辨識:
- 畫面的開始,以及該畫面對應的結束。
- 當您傳入相同權杖時,其他事件屬於同一個畫面。
問題 10/10:遊戲無法擷取詳細的計時資料
先前的 API 無法擷取詳細的計時資料。對您的遊戲而言,您可能想知道某些作業花了多久或何時發生。例如:- GPU 在一個畫面中忙碌了多少時間?
- 畫面何時開始?
- ORIGIN 事件何時發生?
- 畫面何時呈現?
Present呼叫何時抵達 GPU?
解決方案:讓遊戲擷取詳細計時資料的方法
圖 15. 讓遊戲擷取詳細計時資料的方法。
畫面步調 API 的建議用法
本節說明如何調節延遲:找出您目前的延遲、決定您想要的延遲,然後使用畫面步調 API 進行變更。調節延遲
圖 16. 調節延遲:三個簡單步驟。
- 使用畫面統計資料測量遊戲中的理想延遲。
- 為變動性加上一些餘裕。
- 計算參數。
PeriodInIntervals 與 IntervalOffsetInMicroseconds。必須將它們提供給驅動程式。
這些變更是管線化的,因此會在幾個畫面內生效。您可能希望遊戲對延遲上升立即反應,但對延遲下降則緩慢反應。
調節延遲範例
圖 17. 調節延遲範例。
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 進行撕裂。
您可以建立非常複雜的演算法來預測所有可能的情況。
畫面步調 API 的不建議用法
本節說明我們不建議的畫面步調 API 用法。背景緩衝區未在約定時間內呈現
本主題到目前為止涵蓋的問題類似於一份合約。您同意畫面會在特定時間內完成。如果您未在約定時間內完成畫面,就可能發生掉格等問題。以往只有在 CPU 或 GPU 超出預算時才會掉格。現在,您可能做出延遲預測卻未能達成,即使 CPU 與 GPU 都在時間預算內,仍會掉格。遊戲使用的交換鏈太短
當遊戲使用的交換鏈太短時,可能會發生停滯等問題。理想的交換鏈大小是PeriodInIntervals 加一。例如,如果您使用的 PeriodInIntervals 等於 2,且 IntervalOffsetInMicroseconds 為 0,就需要大小為 3 的交換鏈。這是因為您有一個正在顯示的畫面、另一個正處於計算第二個間隔的畫面,以及第三個正處於計算第一個間隔的畫面。總共三個畫面同時存在。嚴格來說,它們需要不同的交換鏈緩衝區,否則可能會發生衝突。
在某些情況下,交換鏈太短不會造成任何損失,什麼事也不會發生。不過,在其他情況下,CPU 會在 PresentX 中停滯,或 GPU 會在 ResourceBarrier 中停滯。有時停滯不會影響畫面播放速率,有時則會。
您可以為上述每種情境建立範例。您選擇哪一種,取決於遊戲的運作方式。
遊戲只在啟動時設定一次 PeriodInIntervals 與 IntervalOffsetInMicroseconds
如果遊戲在啟動時設定新參數PeriodInIntervals 與 IntervalOffsetInMicroseconds,之後便不再處理它們,就可能發生問題。這類似於以前的做法:事先設定目標延遲,然後無論遊戲中發生什麼事都維持相同的選擇。
如果您的延遲選擇過於積極或過於保守,就可能產生損失。過於積極,即使在預算內也會不必要地掉格。過於保守,延遲總是高於所需。
遊戲在 ORIGIN 事件發出訊號前就開始計算畫面
當您排程 ORIGIN 事件並等待它,但遊戲已開始畫面計算時,就會發生此問題。在此情況下,計算並未在您設定的時間開始,因此延遲會來回波動而變得無法確定。這會導致控制器輸入無法同步,也無法在 PIX 中追蹤畫面。在本主題提出的問題中,這是最重要的一項。目前尚不清楚為何在您排程 ORIGIN 事件並等待它時,這只有在某些時候才有效。
示範
PIX System Monitor 中的畫面步調
圖 19. PIX System Monitor 中的畫面步調。
- 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。
- 功能表:
- 位於右上角。
- 分為兩個區段:
- 畫面負載參數。
- 畫面步調參數。
- 畫面負載由計時器強制執行。
- 圖表:
- 位於左上角的三個折線圖。
- 畫面時間/播放速率。
- 理想延遲。
- 延遲餘裕 (安全餘裕)。
- 負值餘裕表示畫面延遲。
- 位於左上角的三個折線圖。
- 時間軸:
- 類似簡化的 PIX 計時擷取。
- 顯示下列資訊。
- CPU 更新。
- CPU 轉譯。
- GPU 圖形佇列。
- GPU 計算佇列。
- Flip。
- Flip 會告訴您正在顯示哪一個交換鏈緩衝區。
- 時間軸圖表依交換鏈緩衝區以色彩區分。
- 圖 20 的範例有三個交換鏈緩衝區,以紫色、綠色與黃色表示。
小數畫面的延遲
先前的 API 不允許小數畫面的延遲。不過,新 API 允許,讓遊戲能以微秒為單位指定小數位移。 圖 21. 小數畫面的延遲 1/2。

延遲與交換鏈大小脫鉤
圖 23. 延遲與交換鏈大小脫鉤 1/2。 使用新 API 時,延遲不再取決於交換鏈長度。此示範顯示下列情況會發生什麼事:- 交換鏈大小為我們建議的大小。
- 交換鏈大小小於我們建議的大小。


Present 時仍在顯示中,CPU 上的 BlockOnFence_SwapThrottle 可能會停滯。如果緩衝區在從 PRESENT 狀態轉換為可寫入狀態時仍在顯示中,GPU 上的 WaitOnSwapChainSurface 也可能停滯。這些作業能延後得越久,所需的緩衝就越少。
您可以在 Frame Pacing Sample 中縮短 CPU Update 與 CPU Render 的時間,使這些停滯發生。
延遲調節
圖 25. 延遲調節 1/4。



後續步驟
如果您想檢閱與本概觀相關的簡報資料,請從 XBOX Developer Download Secure Data Storage (SDS) 下載下列檔案。- 投影片: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 簡報與影片
- 前往 Game Development Kit (GDK) 下載。
- 在 Select file type 下,選取 Conference Materials,然後選取 Confirm。
- 在 Select build/version number 下,選擇下列其中一個圖形簡報。
- Xfest 2022 Graphics Track PowerPoints
- Xfest 2022 Graphics Track Videos
- 選取 Download Now,然後等待 .zip 檔案下載完成。
- 前往本機的 [下載] 資料夾,然後從壓縮資料夾中解壓縮 (擷取) 檔案。
下載本文提及的 Microsoft Game Development Kit 主機範例
- 前往 Game Development Kit (GDK) 下載。
- 在 Select file type 下,選取 GDK,然後選取 Confirm。
- 在 Select build/version number 下,選擇最新的 Microsoft GDKX (Sample only) 選項。
- 選取 Download Now,然後等待 .zip 檔案下載完成。
- 前往本機的 [下載] 資料夾,然後從壓縮資料夾中解壓縮 (擷取) 檔案。
- 選取 GDKSamples > Graphics,然後搜尋該範例。
