> ## 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 One 上的畫面排程與呈現

> XBOX One 上的畫面排程與呈現

* [以直接呈現與畫面排程取代交換鏈結](#ID4SwapChainsRIP)
  * [從 Windows 與 ERA 移轉 ](#ID4Migration)
  * [高階工作流程](#ID4WorkflowPrimer)
  * [範例程式碼](#ID4ExampleCode)

<a id="ID4SwapChainsRIP" />

## 以直接呈現與畫面排程取代交換鏈結

Game OS 已全面改造圖形呈現的程序。其目標如下。

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

新方法提供的部分較值得注意的功能如下。

* 直接呈現可顯示的 ID3D12Resource 物件，讓交換資源的排列與選擇更具彈性，因為鏈結演變為使用者管理的集區。
* 能夠在任何圖形或運算命令佇列上呈現，可消除圖形佇列在處理交換資源後等待由運算佇列發出訊號之呈現柵欄而造成的停滯 (近期將提供支援)。
* 引進以管線權杖形式正式識別畫面的方法。畫面管線權杖提供一種方式，可針對管線持續時間跨越多個垂直同步間隔的畫面，將畫面節流、輸入取樣、CPU 命令清單錄製事件、GPU 轉譯事件與顯示管線事件建立關聯。接著可依權杖查詢個別畫面的一致統計資料，並透過 PIX 呈現。讓系統了解畫面的生命週期 (包括其中發生的所有重要事件)，對 XBOX Game Streaming 服務的監視與調整也更有幫助。
* 以事件為基礎、由中斷驅動的新顯示畫面產生排程，允許以呈現間隔的節奏指定畫面管線中的起始時間，並在錯過畫面時自動進行相位調整。排程允許選擇性指定使用者建立的事件，以擴充發出訊號的方式。
* 以輪詢或封鎖畫面管線起始事件作為正式節流機制的系統。此系統允許選擇性指定使用者建立的事件作為等待的額外條件。
* 以更抽象的方式表示轉譯與呈現頻率，呈現間隔以時間長度而非整數垂直空白間隔來指定。此變更可在顯示輸出速率不固定的情況下 (例如在 60 Hz 或 120 Hz 顯示器上以 60 Hz 轉譯)，於固定的主機硬體上維持一致的畫面轉譯，並讓未來能直接支援高於 60 Hz 頻率的原生轉譯 (例如原生 120 Hz)。
* 擴充所追蹤的畫面統計資料，並提供可篩選、可延伸以因應未來新增項目的報告機制。
* 識別畫面起始時間，搭配未來新增的同步顯示與輸入裝置時鐘 (以避免持續漂移)，讓平台能夠指示由遊戲台 (或其他) 裝置韌體收集輸入樣本，並及時提供給遊戲查詢，以協助將整體延遲降到最低。

<a id="ID4Migration" />

## 從 Windows 與 ERA 移轉

Game OS 不支援的交換鏈結、呈現與排程相關 API 都有對應的替代項目。為協助您快速了解，下表提供一組常用的舊版 API 與參數，並在相鄰欄中列出其對應的替代項目。

| 舊版 | Game OS |
| - | - |
| IDXGIFactory::CreateSwapChain、<br />IDXGIFactory2::CreateSwapChainForCoreWindow 以及 <br />IDXGIFactory2::CreateSwapChainForComposition<br /><br />    - *DXGIX\_SWAP\_CHAIN\_FLAG\_QUANTIZATION\_RGB\_FULL*<br />    - *DXGIX\_SWAP\_CHAIN\_FLAG\_COLORIMETRY\_*<br />*RGB\_BT2020\_ST2084*<br />    - *DXGIX\_SWAP\_CHAIN\_MATCH\_XBOX360\_AND\_PC*<br /><br />     - *DXGIX\_SWAP\_CHAIN\_MATCH\_OTHER\_CONSOLES*<br />    - *DXGIX\_SWAP\_CHAIN\_FLAG\_*<br />*QUANTIZATION\_RGB\_LIMITED*<br />    - *DXGIX\_SWAP\_CHAIN\_FLAG\_*<br />*COLORIMETRY\_sRGB\_TO\_REC709*<br /><br />    - *DXGIX\_SWAP\_CHAIN\_FLAG\_*<br />*AUTOMATIC\_GAMEDVR\_TONEMAP* | [ID3D12Device::CreateCommittedResource](https://learn.microsoft.com/windows/desktop/api/d3d12/nf-d3d12-id3d12device-createcommittedresource)，且 <br />*HeapFlags* 包含 [D3D12\_HEAP\_FLAG\_ALLOW\_DISPLAY](https://learn.microsoft.com/windows/desktop/api/d3d12/ne-d3d12-d3d12_heap_flags)<br /><br /> <br />    - [ColorSpace](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_present_plane_parameters) [DXGI\_COLOR\_SPACE\_RGB\_FULL\_G22\_NONE\_P709](https://learn.microsoft.com/windows/desktop/api/dxgicommon/ne-dxgicommon-dxgi_color_space_type)<br />   - [ColorSpace](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_present_plane_parameters) [DXGI\_COLOR\_SPACE\_RGB\_STUDIO\_G22\_NONE\_P2020](https://learn.microsoft.com/windows/desktop/api/dxgicommon/ne-dxgicommon-dxgi_color_space_type)<br /> <br />    - 不支援 ([DXGI\_COLOR\_SPACE\_RGB\_FULL\_G22\_NONE\_P709](https://learn.microsoft.com/windows/desktop/api/dxgicommon/ne-dxgicommon-dxgi_color_space_type)<br />  不含 *sRGB\_TO\_REC709*)<br />    - [ColorSpace](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_present_plane_parameters) [DXGI\_COLOR\_SPACE\_RGB\_FULL\_G22\_NONE\_P709](https://learn.microsoft.com/windows/desktop/api/dxgicommon/ne-dxgicommon-dxgi_color_space_type)<br />    - 不支援<br /><br />    - 不支援<br /><br /> <br />   - [D3D12XBOX\_RESOURCE\_FLAG\_ALLOW\_](/zh-TW/reference/graphics/d3d12_x/enums/d3d12_resource_flags)<br />[AUTOMATIC\_GAMEDVR\_TONE\_MAP](/zh-TW/reference/graphics/d3d12_x/enums/d3d12_resource_flags) |
| IDXGISwapChain1::SetHDRToneMapper | [ID3D12CommandQueue::SetHDRToneMapperX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12commandqueue/methods/id3d12commandqueue_sethdrtonemapperx) |
| IDXGISwapChain::Present 與 DXGIXPresentArray<br /><br />   - *SyncInterval* 1 - 4<br />    - *SyncInterval* 0<br />    - *PresentImmediateThreshold*<br /><br />    - *DXGI\_PRESENT\_DO\_NOT\_WAIT*<br />    - *DXGIX\_PRESENT\_DISABLE\_VARIABLE\_REFRESH\_RATE*<br />    - *DXGIX\_PRESENT\_PRESERVE\_FRAMEBUFFER*<br />    - *DXGIX\_PRESENT\_UPSCALE\_FILTER\_* \* | [ID3D12CommandQueue::PresentX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12commandqueue/methods/id3d12commandqueue_presentx)<br /><br />   - [ID3D12Device::SetFrameIntervalX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_setframeintervalx)(..., *LengthInMicroseconds*, ...);<br />   - [D3D12XBOX\_PRESENT\_FLAG\_IMMEDIATE](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_present_flags) (PresentX 覆寫)<br />    - [ImmediateThresholdPercent](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_present_parameters)<br /><br />    - [D3D12XBOX\_PRESENT\_FLAG\_DO\_NOT\_WAIT](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_present_flags)<br />    - [D3D12XBOX\_PRESENT\_FLAG\_DISABLE\_VARIABLE\_REFRESH\_RATE](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_present_flags)<br />    - [D3D12XBOX\_PRESENT\_FLAG\_PERSIST](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_present_flags)<br />    - [D3D12XBOX\_PRESENT\_PLANE\_SCALE\_FILTER](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_present_plane_scale_filter) |
| IDXGISwapChain::GetFrameStatistics 與 DXGIXGetFrameStatistics | [ID3D12Device::GetFrameStatisticsX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_getframestatisticsx) |
| DXGIX\_FRAME\_STATISTICS<br /><br />    - *GPUCountTitleUsed*<br /><br /><br /> | [D3D12XBOX\_RENDER\_STATISTICS](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_render_statistics)<br /><br />    - *GPUBusyDuration* x [GetTimeStampFrequency](https://learn.microsoft.com/windows/desktop/api/d3d12/nf-d3d12-id3d12commandqueue-gettimestampfrequency)() / <br />QueryPerformanceFrequency().QuadPart <br />(涵蓋畫面間隔) |
| DXGIX\_FRAME\_STATISTICS<br /><br />    - *CPUTimePresentCalled*<br />    - *CPUTimeAddedToQueue*<br />    - *CPUTimeFrameComplete*<br />    - *QueueLengthAddedToQueue*<br /> | [D3D12XBOX\_PRESENT\_STATISTICS](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_present_statistics)<br /><br />    - *PresentCallTime*<br />    - *PresentQueueTime*<br />    - *GPUProcessTime*<br />    - *QueuedFrameCount* |
| DXGIX\_FRAME\_STATISTICS<br /><br />    - *CPUTimeVSync*<br />    - *CPUTimeFlip*<br />    - *PercentScanned*<br />    - *VSyncCount* | [D3D12XBOX\_DISPLAY\_STATISTICS](/zh-TW/reference/graphics/d3d12_x/structs/d3d12xbox_display_statistics)<br /><br />    - *VSyncTime*<br />    - *FlipTime*<br />    - *PercentScanned*<br />    - 不支援 |
| DXGIX\_FRAME\_STATISTICS<br /><br />    - *Cookie*<br /><br /><br /><br />    - *VSyncCount* | <br /><br />    - [D3D12XBOX\_FRAME\_PIPELINE\_TOKEN](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_frame_pipeline_token) 作為 <br />畫面識別碼 (與自訂 <br />Cookie 的關聯必須手動執行)<br /><br />    - 不支援 |
| DXGIXSetVLineNotification<br /><br />   - VLINECOUNTER0<br />   - VLINECOUNTER1 | [ID3D12Device::ScheduleFrameEventX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_scheduleframeeventx)<br /><br />   - [D3D12XBOX\_FRAME\_EVENT\_DISPLAY\_VBLANK](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_frame_event_type)<br />   - [D3D12XBOX\_FRAME\_EVENT\_ORIGIN](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_frame_event_type) |
| DXGIXSetFrameNotification<br /><br />   - FRAME\_NOTIFICATION\_FLIPPED | ID3D12Device::ScheduleFrameEventX<br /><br />   - [D3D12XBOX\_FRAME\_EVENT\_DISPLAY\_FLIP](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_frame_event_type) |
| 以 DXGIXSetVLineNotification (或 Present) 作為節流機制 | [ID3D12Device::ScheduleFrameEventX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_scheduleframeeventx) 與 [ID3D12Device::WaitFrameEventX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_waitframeeventx)，搭配 [D3D12XBOX\_FRAME\_EVENT\_ORIGIN](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_frame_event_type) |

<a id="ID4WorkflowPrimer" />

## 高階工作流程

建立可顯示資源集區、將畫面排程作為明確的節流機制，以及將畫面資源呈現到顯示器的一般方法，包含幾個初始化步驟，接著是遊戲迴圈中的一連串作業。如需每個步驟的詳細資訊 (詳細說明、影響與建議用法)，請參閱參考頁面。

**初始化**

* 呼叫 [ID3D12Device::CreateCommittedResource](https://learn.microsoft.com/windows/desktop/api/d3d12/nf-d3d12-id3d12device-createcommittedresource)，並在 *HeapFlags* 中包含 [D3D12\_HEAP\_FLAG\_ALLOW\_DISPLAY](https://learn.microsoft.com/windows/desktop/api/d3d12/ne-d3d12-d3d12_heap_flags)，以建立一組 [ID3D12Resource](https://learn.microsoft.com/windows/desktop/api/d3d12/nn-d3d12-id3d12resource) 物件，作為支援顯示呈現的可交換資源集區 (也就是「交換鏈結」)。
* 呼叫 [ID3D12Device::SetFrameIntervalX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_setframeintervalx) 以建立畫面重新整理速率與週期。*LengthInMicroseconds* 定義畫面呈現到顯示器 (翻轉) 之間的時間間隔。*PeriodInIntervals* 以整數畫面間隔定義 CPU 允許領先 GPU 的程度。CPU 在受到節流之前，允許領先 GPU *PeriodInIntervals* - 1 個畫面 (例如，*PeriodInIntervals* 為 2 時，允許 CPU 在 GPU 處理為畫面 *n* 提交的命令時，錄製並提交畫面 *n* + 1 的轉譯命令)。
* 呼叫 [ID3D12Device::ScheduleFrameEventX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_scheduleframeeventx) 以建立在畫面間隔內節流 CPU 的時間點。指定 [D3D12XBOX\_FRAME\_EVENT\_ORIGIN](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_frame_event_type) 的 Type 以及 *IntervalOffsetInMicroseconds* 時間位移，以排程畫面的起始事件。CPU 會受到節流，直到間隔內經過指定的 *IntervalOffsetInMicroseconds* 時間量為止。

**遊戲迴圈**

* 以 [D3D12XBOX\_FRAME\_EVENT\_ORIGIN](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_frame_event_type) 的 Type 呼叫 [ID3D12Device::WaitFrameEventX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_waitframeeventx) 以執行 CPU 節流。將 *TimeOutInMs* 指定為 **D3D12XBOX\_WAIT\_FRAME\_EVENT\_INFINITE** 以封鎖直到起始時間到達，或指定以毫秒為單位的固定逾時來輪詢起始事件 (如果起始時間尚未到達，會傳回 **S\_FALSE**)。為 *pToken* 指定 [D3D12XBOX\_FRAME\_PIPELINE\_TOKEN](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_frame_pipeline_token) 的指標，在起始時間到達而傳回時，會將其設定為唯一識別該畫面的權杖值。必須為該畫面追蹤此權杖，並在將其呈現到顯示器時提供。
* 查詢控制器輸入，並將畫面轉譯到以顯示支援建立的 [ID3D12Resource](https://learn.microsoft.com/windows/desktop/api/d3d12/nn-d3d12-id3d12resource) 物件。
* 呼叫 [ID3D12CommandQueue::PresentX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12commandqueue/methods/id3d12commandqueue_presentx)，將轉譯完成的畫面資源排入佇列以呈現到顯示器。資源與畫面權杖會在 *pPlaneParameters* 陣列中提供。
* 使用預期最後一個已到達顯示器之畫面的權杖呼叫 [ID3D12Device::GetFrameStatisticsX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_getframestatisticsx) (或針對感興趣的不同畫面進行多次呼叫)。分析傳回的畫面統計資料，據以更新已建立的畫面節流點，目標是將從控制器輸入到顯示的延遲降到最低。以 [D3D12XBOX\_FRAME\_EVENT\_ORIGIN](/zh-TW/reference/graphics/d3d12_x/enums/d3d12xbox_frame_event_type) 類型呼叫 [ID3D12Device::ScheduleFrameEventX](/zh-TW/reference/graphics/d3d12_x/interfaces/id3d12device/methods/id3d12device_scheduleframeeventx) 來執行更新，這些更新會在呼叫後下一個要呈現的畫面翻轉到顯示器時套用。
* 在繼續等待下一個畫面起始時間之前 (或在輪詢期間)，於 CPU/GPU 上執行任何與輸入無關的工作。

<a id="ID4ExampleCode" />

## 範例程式碼

下列程式碼範例示範 1080p、單一背景平面、包含 2 個資源的交換集區以 60 FPS 轉譯的高階工作流程。

```cpp theme={null}
    // Game initialization.

    HRESULT hr;

    D3D12_HEAP_PROPERTIES heapProperties = {};
    heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
    heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
    heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
    heapProperties.CreationNodeMask = D3D12XBOX_NODE_MASK;
    heapProperties.VisibleNodeMask = D3D12XBOX_NODE_MASK;

    D3D12_RESOURCE_DESC swapResourceDesc = {};
    swapResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    swapResourceDesc.Alignment = 0;
    swapResourceDesc.Width = 1920;
    swapResourceDesc.Height = 1080;
    swapResourceDesc.DepthOrArraySize = 1;
    swapResourceDesc.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
    swapResourceDesc.MipLevels = 1;
    swapResourceDesc.SampleDesc.Count = 1;
    swapResourceDesc.SampleDesc.Quality = 0;
    swapResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
    swapResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;

    // Create a swap pool of two displayable resources.

    ID3D12Resource* pSwapResources[2];

    for (UINT i = 0; i < 2; ++i)
    {
        hr = pd3dDevice->CreateCommittedResource(
            &heapProperties,
            D3D12_HEAP_FLAG_ALLOW_DISPLAY,
            &swapResourceDesc,
            D3D12_RESOURCE_STATE_PRESENT,
            nullptr,
            __uuidof(ID3D12Resource),
            (void**)&pSwapResources[i]);

        if (FAILED(hr))
        {
            return hr;
        }
    }

    // Establish a 60 Hz frame-rate interval and a frame period spanning two intervals, which permits the
    // CPU to begin recording and submitting commands for one frame while the GPU is still processing
    // commands submitted on the previous frame (if the CPU attempts to begin work on frame n + 2 before
    // the GPU has completed frame n and flipped it to the display, it will be throttled until the interval
    // following the flip).

    hr = pd3dDevice->SetFrameIntervalX(
        nullptr,
        D3D12XBOX_FRAME_INTERVAL_60_HZ,         // 16,667 microseconds between flips (60 flips per second).
        2,                                      // Frame period of 2 intervals (frame started during one
                                                // interval and finished on the next).
        D3D12XBOX_FRAME_INTERVAL_FLAG_NONE);

    if (FAILED(hr))
    {
        return hr;
    }

    // Schedule a frame origin event to establish the CPU throttle point for the game. The frame
    // origin is the time that a new frame is generated, and is generally expected to be the
    // moment when input is queried and directly used in the construction of the frame. The
    // origin should be scheduled as near to the time of frame display as possible to minimize latency,
    // based on expected frame duration (and informed by statistics feedback). In this example,
    // the game anticipates an initial end-to-end frame duration (from input query, to CPU rendering,
    // to GPU processing, to presentation for display) of 27,778 microseconds (1.66 frame intervals).
    // This translates into a frame period of 2 intervals and an interval offset of 5,556 microseconds.

    hr = pd3dDevice->ScheduleFrameEventX(
        D3D12XBOX_FRAME_EVENT_ORIGIN,              // Origin event type.
        5556,                                      // Begin the frame after one-third of the interval has passed.
        nullptr,                                   // Don't signal any additional game events at the origin.
        D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE);

    if (FAILED(hr))
    {
        return hr;
    }

    {...}

    // Game loop.

    D3D12XBOX_FRAME_PIPELINE_TOKEN frameToken = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL;

    do
    {
        // Perform a modest, measured amount of CPU/GPU processing not directly dependent on user input
        // (for example, texture streaming).
        {...}

        // Poll on the origin event for the frame, throttling the CPU as necessary.

        hr = pd3dDevice->WaitFrameEventX(
            D3D12XBOX_FRAME_EVENT_ORIGIN,
            0,                                    // Poll with an immediate timeout.
            nullptr,                              // Don't wait on additional game events in
                                                  // conjunction with origin.
            D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE,
            &token);                              // Obtain the frame's identifying token.

        if (hr == D3DERR_DEVICEREMOVED)
        {
            // The GPU may have stopped responding, preventing it from reaching the present submitted for
            // a previous frame and flipping it to the display. This would in turn prevent
            // the new frame origin event from being signaled due to the throttle. Alternatively,
            // another thread responsible for processing suspend events may have called
            // SuspendX during the origin wait, causing it to fail. Test and handle these
            // conditions as appropriate.
            {...}
        }

    } while (hr == S_FALSE);

    ++m_FrameIndex;

    // Store the frame token for later statistics retrieval.

    m_FrameToken[m_FrameIndex % 16] = token;

    // Query user input from the gamepad.

    IGameInputReading* pReading;

    pGameInput->GetCurrentReading(
        GameInputKindGamepad,
        pGamepadDevice,
        &pReading);

    // Render the frame based on the input reading to pSwapResources[m_FrameIndex % 2].

    {...}

    pReading->Release();

    // Queue the rendered frame for presentation to the display. Effective frame scheduling
    // will contrive to minimize the amount of time the swap resource remains in the queue
    // before being flipped and scanned out.

    D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParameters = {};

    planeParameters.Token = token;                          // Identify the frame being presented.
    planeParameters.ResourceCount = 1;
    planeParameters.ppResources = &pSwapResources[m_FrameIndex % 2]; // Present the associated swap resource.
    planeParameters.pSrcViewRects = nullptr;                // Display the entire resource (no dynamic scaling
                                                            // or split-screen multiplayer).
    planeParameters.pDestPlacementBase = nullptr;           // Full coverage of the display.
    planeParameters.ColorSpace = DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 // HDR color space.
    planeParameters.ScaleFilter = D3D12XBOX_PRESENT_PLANE_SCALE_FILTER_DEFAULT;
    planeParameters.Flags = D3D12XBOX_PRESENT_PLANE_FLAG_NONE;

    D3D12XBOX_PRESENT_PARAMETERS presentParameters = {};

    presentParameters.ImmediateThresholdPercent = 5.0f;     // Allow tearing in the top 5 percent of the frame.
    presentParameters.ViewCount = 1;                        // No split-screen multiplayer.
    presentParameters.Flags = D3D12XBOX_PRESENT_FLAG_NONE;

    pd3dCommandQueue->PresentX(
        1,
        &planeParameters,

    // Collect statistics for the most recently flipped frame.

    D3D12XBOX_FRAME_STATISTICS statisticsList[4];
    UINT statisticsCount = _countof(statisticsList);

    hr = pd3dDevice->GetFrameStatisticsX(
        m_FrameToken[(m_FrameIndex + 14) % 16],             // Query the frame originating 2 intervals in the past
                                                            // (display flip guaranteed).
        D3D12XBOX_FRAME_STATISTICS_TYPE_FRAME_EVENT |
            D3D12XBOX_FRAME_STATISTICS_TYPE_RENDER |
            D3D12XBOX_FRAME_STATISTICS_TYPE_PRESENT |
            D3D12XBOX_FRAME_STATISTICS_TYPE_DISPLAY,        // Query statistics relevant to latency analysis.
        &statisticsCount,                                   // Four entries is sufficient given no overlay and
        statisticsList);                                    // a single frame event type scheduled.

    if (SUCCEEDED(hr) &&
        (statisticsCount != 0))
    {
        bool originUpdate = false;
        UINT intervalOffsetInUs;

        // Analyze the returned statistics list to determine such things as frame length from origin to GPU
        // completion and display. Use the information to inform updates to frame scheduling with the goals
        // of minimizing latency while avoiding missed vblanks due to processing spikes.

        // Some possible considerations:
        //   Is D3D12XBOX_PRESENT_STATISTICS::GPUProcessTime too far from D3D12XBOX_DISPLAY_STATISTICS::VSyncTime?
        //     (Wasted latency from early frame completion.)
        //   Is D3D12XBOX_PRESENT_STATISTICS::GPUProcessTime too near to D3D12XBOX_DISPLAY_STATISTICS::VSyncTime?
        //     (Risk of frame drop from late frame completion.)
        //   Is D3D12XBOX_PRESENT_STATISTICS::GPUProcessTime not arriving soon after D3D12XBOX_RENDER_STATISTICS::
        //     GPUWriteCompleteTime? (Frame independent work done between swap resource completion and present
        //     instead of after present.)
        //   Is D3D12XBOX_FRAME_EVENT_STATISTICS::WaitCallTime too far from D3D12XBOX_FRAME_EVENT_STATISTICS::
        //     SignalTime? (Wait thread blocked on origin signal.)

        // When frame latency conditions have changed beyond some threshold margin, update the frame origin to
        // move it nearer to the target vblank (reduce latency) or further fom the vblank (avoid vblank miss).  
        // The frame origin update will be applied when the swap resource of the subsequent frame
        // (m_FrameIndex + 1) has been flipped to the display.

        if (originUpdate)
        {
            pd3dDevice->ScheduleFrameEventX(
                D3D12XBOX_FRAME_EVENT_ORIGIN,
                intervalOffsetInUs,
                nullptr,
                D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE);
        }
    }
```

<a id="ID4EABAC" />


## Related topics

- [NDA 主題目錄 - Microsoft Learn 線上連結](/zh-TW/nda/nda-topic-directory.md)
- [媒體驗證測試步驟](/zh-TW/publishing/game-publishing/concepts/certification/certification-media-validation-test-steps.md)
- [認證主機 BVT 指南](/zh-TW/publishing/game-publishing/concepts/certification/certification-console-bvt-guide.md)
- [PFLeaderboardsGetEntityLeaderboardResponse](/zh-TW/services/playfab/api-references/c/pfleaderboardstypes/structs/pfleaderboardsgetentityleaderboardresponse.md)
- [XBOX 遊戲的 XBOX 需求](/zh-TW/publishing/certification/xbox-requirements.md)
