Reduzieren der Latenz mit DXGI 1.3-SwapchainsReduce latency with DXGI 1.3 swap chains

Verwenden Sie DXGI 1.3 zum Reduzieren der geltenden Framelatenz, indem Sie warten, bis die Swapchain den geeigneten Zeitpunkt signalisiert, um mit dem Rendern eines neuen Frames zu beginnen.Use DXGI 1.3 to reduce the effective frame latency by waiting for the swap chain to signal the appropriate time to begin rendering a new frame. Spiele müssen in der Regel die geringstmögliche Latenz aufweisen, was den Zeitraum vom Eingang der Spielereingabe bis zur Reaktion des Spiels auf die Eingabe betrifft, indem die Anzeige aktualisiert wird.Games typically need to provide the lowest amount of latency possible from the time the player input is received, to when the game responds to that input by updating the display. In diesem Thema wird ein Verfahren erläutert, das ab Version Direct3D 11.2 verfügbar ist. Damit können Sie in Ihrem Spiel die geltende Framelatenz verringern.This topic explains a technique available starting in Direct3D 11.2 that you can use to minimize the effective frame latency in your game.

Wie kann mit dem Warten auf den Hintergrundpuffer die Latenz reduziert werden?How does waiting on the back buffer reduce latency?

Bei der Flipmodell-Swapchain werden „Flips“ des Hintergrundpuffers jeweils in die Warteschlange eingereiht, wenn vom Spiel IDXGISwapChain::Present aufgerufen wird.With the flip model swap chain, back buffer "flips" are queued whenever your game calls IDXGISwapChain::Present. Wenn von der Renderschleife „Present()“ aufgerufen wird, blockiert das System den Thread, bis die Darstellung eines vorherigen Frames abgeschlossen ist. So wird in der Warteschlange Platz für den neuen Frame geschaffen, bevor dieser dargestellt wird.When the rendering loop calls Present(), the system blocks the thread until it is done presenting a prior frame, making room to queue up the new frame, before it actually presents. Dies verursacht zusätzliche Latenz zwischen dem Zeitpunkt, zu dem vom Spiel ein Frame gezeichnet wird, und dem Zeitpunkt, zu dem die Anzeige des Frames vom System zugelassen wird.This causes extra latency between the time the game draws a frame and the time the system allows it to display that frame. In vielen Fällen wird vom System ein stabiles Gleichgewicht erreicht, bei dem vom Spiel zwischen dem Rendern und Darstellen des Frames immer nahezu einen ganzen zusätzlichen Frame lang abgewartet wird.In many cases, the system will reach a stable equilibrium where the game is always waiting almost a full extra frame between the time it renders and the time it presents each frame. Es ist besser zu warten, bis das System zum Akzeptieren eines neuen Frames bereit ist, als den Frame basierend auf den aktuellen Daten zu rendern und sofort in die Warteschlange einzureihen.It's better to wait until the system is ready to accept a new frame, then render the frame based on current data and queue the frame immediately.

Erstellen eine mit wartemöglichkeit SwapChain mit der DXGI_SWAP_Kette_FLAG_FRAME_LATENZ_WAITABLE_Objekt Flag.Create a waitable swap chain with the DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT flag. Swapchains, die auf diese Art erstellt werden, können Ihre Renderschleife benachrichtigen, wenn das System zum Akzeptieren eines neuen Frames bereit ist.Swap chains created this way can notify your rendering loop when the system is actually ready to accept a new frame. So kann das Spiel anhand der aktuellen Daten rendern und das Ergebnis sofort in die vorhandene Warteschlange einfügen.This allows your game to render based on current data and then put the result in the present queue right away.

Schritt 1: Erstellen einer SwapChain mit mit wartemöglichkeitStep 1: Create a waitable swap chain

Geben Sie die DXGI_AUSTAUSCHEN_Kette_FLAG_FRAME_LATENZ_WAITABLE_Objekt kennzeichnen Sie beim Aufrufen CreateSwapChainForCoreWindow.Specify the DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT flag when you call CreateSwapChainForCoreWindow.

swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; // Enable GetFrameLatencyWaitableObject().

Beachten Sie    im Gegensatz zu einigen Flags die, dieses Flag kann nicht hinzugefügt oder entfernt werden mithilfe von ResizeBuffers.Note   In contrast to some flags, this flag can't be added or removed using ResizeBuffers. Von DXGI wird ein Fehlercode zurückgegeben, wenn dieses Flag anders als bei der Erstellung der Swapchain festgelegt wird.DXGI returns an error code if this flag is set differently from when the swap chain was created.

 

// If the swap chain already exists, resize it.
HRESULT hr = m_swapChain->ResizeBuffers(
    2, // Double-buffered swap chain.
    static_cast<UINT>(m_d3dRenderTargetSize.Width),
    static_cast<UINT>(m_d3dRenderTargetSize.Height),
    DXGI_FORMAT_B8G8R8A8_UNORM,
    DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT // Enable GetFrameLatencyWaitableObject().
    );

Schritt 2: Legen Sie die Frame-LatenzStep 2: Set the frame latency

Legen Sie die Framelatenz mit der IDXGISwapChain2::SetMaximumFrameLatency-API fest, anstatt IDXGIDevice1::SetMaximumFrameLatency aufzurufen.Set the frame latency with the IDXGISwapChain2::SetMaximumFrameLatency API, instead of calling IDXGIDevice1::SetMaximumFrameLatency.

Standardmäßig ist die Framelatenz für Swapchains mit Wartemöglichkeit auf 1 festgelegt. Dies führt zur geringstmöglichen Latenz, jedoch auch zu einer Reduzierung der CPU-GPU-Parallelität.By default, the frame latency for waitable swap chains is set to 1, which results in the least possible latency but also reduces CPU-GPU parallelism. Falls Sie eine höhere CPU-GPU-Parallelität benötigen, um 60 F/s zu erzielen – also wenn die CPU und GPU jeweils weniger als 16,7 ms pro Frame für die Verarbeitung des Renderns aufwendet, die Summe jedoch größer als 16,7 ms ist – legen Sie die Framelatenz auf 2 fest.If you need increased CPU-GPU parallelism to achieve 60 FPS - that is, if the CPU and GPU each spend less than 16.7 ms a frame processing rendering work, but their combined sum is greater than 16.7 ms — set the frame latency to 2. Auf diese Weise können von der GPU Verarbeitungsschritte ausgeführt werden, die von der CPU während des vorherigen Frames in die Warteschlange eingereiht wurden, während die CPU unabhängig davon gleichzeitig Renderbefehle für den aktuellen Frame übermitteln kann.This allows the GPU to process work queued up by the CPU during the previous frame, while at the same time allowing the CPU to submit rendering commands for the current frame independently.

// Swapchains created with the DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT flag use their
// own per-swapchain latency setting instead of the one associated with the DXGI device. The
// default per-swapchain latency is 1, which ensures that DXGI does not queue more than one frame
// at a time. This both reduces latency and ensures that the application will only render after
// each VSync, minimizing power consumption.
//DX::ThrowIfFailed(
//    swapChain2->SetMaximumFrameLatency(1)
//    );

Schritt 3: Das Objekt mit wartemöglichkeit aus der SwapChain zu erhalten.Step 3: Get the waitable object from the swap chain

Rufen Sie IDXGISwapChain2::GetFrameLatencyWaitableObject auf, um das „wait“-Handle abzurufen.Call IDXGISwapChain2::GetFrameLatencyWaitableObject to retrieve the wait handle. Das „wait“-Handle ist ein Zeiger auf das Objekt mit Wartemöglichkeit.The wait handle is a pointer to the waitable object. Speichern Sie dieses Handle für die Verwendung durch die Renderschleife.Store this handle for use by your rendering loop.

// Get the frame latency waitable object, which is used by the WaitOnSwapChain method. This
// requires that swap chain be created with the DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT
// flag.
m_frameLatencyWaitableObject = swapChain2->GetFrameLatencyWaitableObject();

Schritt 4: Warten Sie vor dem Rendern jeder frameStep 4: Wait before rendering each frame

Die Renderschleife sollte warten, bis die Swapchain über das Objekt mit Wartemöglichkeit ein Signal sendet, bevor sie mit dem Rendern eines Frames beginnt.Your rendering loop should wait for the swap chain to signal via the waitable object before it begins rendering every frame. Dies gilt auch für den ersten Frame, der mit der Swapchain gerendert wird.This includes the first frame rendered with the swap chain. Verwenden Sie WaitForSingleObjectEx, und stellen Sie das in Schritt 2 abgerufene „wait“-Handle bereit, um den Start eines Frames zu signalisieren.Use WaitForSingleObjectEx, providing the wait handle retrieved in Step 2, to signal the start of each frame.

Im folgenden Beispiel wird die Renderschleife aus dem DirectXLatency-Beispiel veranschaulicht:The following example shows the render loop from the DirectXLatency sample:

while (!m_windowClosed)
{
    if (m_windowVisible)
    {
        // Block this thread until the swap chain is finished presenting. Note that it is
        // important to call this before the first Present in order to minimize the latency
        // of the swap chain.
        m_deviceResources->WaitOnSwapChain();

        // Process any UI events in the queue.
        CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);

        // Update app state in response to any UI events that occurred.
        m_main->Update();

        // Render the scene.
        m_main->Render();

        // Present the scene.
        m_deviceResources->Present();
    }
    else
    {
        // The window is hidden. Block until a UI event occurs.
        CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
    }
}

Im folgenden Beispiel wird der WaitForSingleObjectEx-Aufruf aus dem DirectXLatency-Beispiel veranschaulicht:The following example shows the WaitForSingleObjectEx call from the DirectXLatency sample:

// Block the current thread until the swap chain has finished presenting.
void DX::DeviceResources::WaitOnSwapChain()
{
    DWORD result = WaitForSingleObjectEx(
        m_frameLatencyWaitableObject,
        1000, // 1 second timeout (shouldn't ever occur)
        true
        );
}

Wie soll sich das Spiel verhalten, während es auf die Swapchain wartet?What should my game do while it waits for the swap chain to present?

Falls Ihr Spiel nicht über Aufgaben verfügt, die zu einer Blockierung der Renderschleife führen, kann die Nutzung des Wartens auf die Swapchain vorteilhaft sein, weil so Energie gespart wird. Dies ist besonders für mobile Geräte wichtig.If your game doesn’t have any tasks that block on the render loop, letting it wait for the swap chain to present can be advantageous because it saves power, which is especially important on mobile devices. Andernfalls können Sie das Multithreading nutzen, um Verarbeitungsschritte auszuführen, während das Spiel auf die Swapchain wartet.Otherwise, you can use multithreading to accomplish work while your game is waiting for the swap chain to present. Unten sind einige Aufgaben aufgeführt, die vom Spiel ausgeführt werden können:Here are just a few tasks that your game can complete:

  • Verarbeiten von NetzwerkereignissenProcess network events
  • Aktualisieren der künstlichen IntelligenzUpdate the AI
  • CPU-basierte PhysikCPU-based physics
  • Rendern mit verzögertem Kontext (auf unterstützten Geräten)Deferred-context rendering (on supported devices)
  • Laden von RessourcenAsset loading

Weitere Informationen zur Programmierung mit Multithreading unter Windows finden Sie in den folgenden verwandten Themen.For more information about multithreaded programming in Windows, see the following related topics.