Ridurre la latenza con catene di scambio DXGI 1.3

Applies to Windows only

Usa DXGI 1.3 per ridurre la latenza effettiva dei fotogrammi attendendo che la catena di scambio segnali il momento appropriato per iniziare il rendering di un nuovo fotogramma. Nei giochi in genere è necessario che latenza sia il più bassa possibile, dal momento in cui viene ricevuto l'input del giocatore a quando il gioco risponde all'input aggiornando lo schermo. Questo argomento spiega una tecnica disponibile a partire da Direct3D 11.2, che puoi usare per ridurre al minimo la latenza effettiva dei fotogrammi nel tuo gioco.

In che modo l'attesa nel buffer nascosto riduce la latenza?

Con la catena di scambio di modello capovolto, i "capovolgimenti" del buffer nascosto vengono accodati ogni volta che il gioco chiama IDXGISwapChain::Present. Quando il ciclo di rendering chiama Present(), il sistema blocca il thread finché non ha terminato di presentare un fotogramma precedente, liberando spazio per inserire in coda il nuovo fotogramma, prima che venga effettivamente presentato. Questo causa una latenza extra tra il momento in cui il gioco disegna un fotogramma e il momento in cui il sistema gli consente di visualizzarlo. In molti casi, il sistema raggiungerà un equilibrio stabile in cui il gioco è sempre in attesa di un fotogramma extra completo tra il momento in cui esegue il rendering e il momento in cui presenta ogni fotogramma. È meglio attendere fino a quando il sistema è pronto per accettare un nuovo fotogramma, quindi eseguire il rendering in base ai dati correnti e accodare immediatamente il fotogramma.

Creare una catena di scambio waitable con il flag DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT. Le catene di scambio create in questo modo possono inviare una notifica al ciclo di rendering quando il sistema è effettivamente pronto per accettare un nuovo fotogramma. Questo consente al gioco di eseguire il rendering in base ai dati correnti e quindi inserire immediatamente il risultato nella coda presente.

Questa esercitazione usa il codice di Esempio di DirectXLatency.

Passaggio 1: creare una catena di scambio waitable

Specifica il flag DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT quando chiami CreateSwapChainForCoreWindow.


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


Nota  Diversamente da altri, questo flag non può essere aggiunto o rimosso con ResizeBuffers. DXGI restituisce un codice di errore se questo flag viene impostato diversamente da quando la catena di scambio è stata creata.


// 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().
    );


Passaggio 2: impostare la latenza dei fotogrammi

Imposta la latenza dei fotogrammi con l'API IDXGISwapChain2::SetMaximumFrameLatency, invece di chiamare IDXGIDevice1::SetMaximumFrameLatency.

Per impostazione predefinita, la latenza dei fotogrammi per le catene di scambio waitable è impostata su 1, che restituisce la minor latenza possibile, ma riduce anche il parallelismo CPU-GPU. Ti serve un parallelismo CPU-GPU più alto per ottenere 60 FPS, ovvero, se la CPU e la GPU impiegano ciascuna meno di 16,7 ms a fotogramma per elaborare il rendering, ma la somma dei due valori è maggiore di 16,7 ms, imposta la latenza dei fotogrammi su 2. Questo permette alla GPU di elaborare il lavoro inserito in coda dalla CPU durante il fotogramma precedente, consentendo nello stesso tempo alla CPU di inviare indipendentemente i comandi di rendering per il fotogramma corrente.


// 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)
//    );


Passaggio 3: ottenere l'oggetto waitable dalla catena di scambio

Chiama IDXGISwapChain2::GetFrameLatencyWaitableObject per recuperare l'handle di attesa. L'handle di attesa è un puntatore all'oggetto waitable. Archivia questo handle per il ciclo di rendering.


// 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();


Passaggio 4: attendere prima di eseguire il rendering di ogni fotogramma

Il ciclo di rendering deve attendere la segnalazione della catena di scambio tramite l'oggetto waitable prima di iniziare il rendering di ogni fotogramma. Questo include il primo fotogramma di cui è stato eseguito il rendering con la catena di scambio. Usa WaitForSingleObjectEx, specificando l'handle di attesa recuperato nel passaggio 2, per segnalare l'inizio di ogni fotogramma.

L'esempio seguente mostra il ciclo di rendering dell'esempio DirectXLatency:


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);
    }
}


L'esempio seguente mostra la chiamata a WaitForSingleObjectEx dell'esempio di DirectXLatency:


// 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
        );
}


Che cosa deve fare il gioco mentre attende la presentazione della catena di scambio?

Se il gioco non ha attività che bloccano il ciclo di rendering, lasciare che attenda la presentazione della catena di scambio può essere vantaggioso perché si risparmia energia, cosa particolarmente importante sui dispositivi mobili. Altrimenti puoi usare il multithreading per eseguire operazioni mentre il gioco attende la presentazione della catena di scambio. Ecco alcune delle attività che il gioco può eseguire:

  • Elaborare gli eventi di rete
  • Aggiornare l'IA
  • Fisica basata sulla CPU
  • Rendering di contesto rinviato (nei dispositivi supportati)
  • Caricamento di asset

Per altre informazioni sulla programmazione a thread multipli in Windows, vedi gli argomenti correlati seguenti.

Argomenti correlati

Esempio di DirectXLatency
IDXGISwapChain2::GetFrameLatencyWaitableObject
WaitForSingleObjectEx
Windows.System.Threading
Programmazione asincrona in C++
Processi e thread
Sincronizzazione
Uso di oggetti evento (Windows)

 

 

Mostra:
© 2014 Microsoft