Сокращение задержки с помощью цепочек буферов DXGI 1.3

Applies to Windows only

DXGI 1.3 позволяет сократить фактическую задержку кадров путем ожидания от цепочки буферов сигнала о подходящем моменте для начала отрисовки нового кадра. Обычно в играх необходимо обеспечить минимальную задержку с момента получения ввода от игрока до момента отклика игры (обновления экрана). В этом разделе описан метод, доступный, начиная с Direct3D 11.2, при помощи которого можно свести к минимуму фактическую задержку кадров в игре.

Каким образом ожидание заднего буфера сокращает задержку?

При использовании цепочки буферов на основе модели отражений "отражения" заднего буфера ставятся в очередь каждый раз, когда игра вызывает функцию IDXGISwapChain::Present. Когда цикл отрисовки вызывает Present(), система блокирует поток, пока не закончится отображение предыдущего кадра, освобождая место в очереди для нового кадра, до его фактического отображения. Такая логика создает дополнительную задержку между моментом отрисовки кадра в игре и моментом, когда система разрешит отобразить этот кадр. Во многих случаях система достигает устойчивого равновесия, при котором в игре всегда имеется период ожидания между временем отрисовки и временем отображения каждого кадра величиной почти в полный дополнительный кадр. Лучше подождать, пока система будет готова принять новый кадр, затем выполнить отрисовку по текущим данным и сразу поставить кадр в очередь.

Создание цепочки буферов с ожиданием при помощи флага DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT. Созданные таким способом цепочки буферов могут уведомлять цикл отрисовки, когда система фактически готова принять новый кадр. В итоге игра может выполнить отрисовку по текущим данным и затем сразу поставить результирующий кадр в очередь на отображение.

В этом пошаговом руководстве использован код из примера DirectXLatency.

Шаг 1. Создание цепочки буферов с ожиданием

Задайте флаг DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT при вызове CreateSwapChainForCoreWindow.


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


Примечание  В отличие от других флагов этот флаг нельзя добавить или удалить с помощью ResizeBuffers. DXGI возвращает код ошибки, если значение этого флага отличается от установленного во время создания цепочки буферов.


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


Шаг 2. Настройка задержки кадров

Задайте задержку кадров при помощи API IDXGISwapChain2::SetMaximumFrameLatency вместо вызова IDXGIDevice1::SetMaximumFrameLatency.

По умолчанию задержка кадров для цепочек буферов с ожиданием установлена равной 1, что обеспечивает минимальную задержку, но также снижает степень параллелизма ЦП и графического процессора. Если вам требуется более высокая степень такого параллелизма для достижения скорости 60 кадров/с — то есть если ЦП и графический процессор в отдельности затрачивают менее 16,7 мс на отрисовку кадров, но их общее суммарное время превышает 16,7 мс, — задайте задержку кадров равной 2. Это позволяет графическому процессору выполнять задачи, поставленные в очередь ЦП во время предыдущего кадра, а ЦП — независимо передавать команды для отрисовки текущего кадра.


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


Шаг 3. Получение объекта ожидания от цепочки буферов

Вызовите IDXGISwapChain2::GetFrameLatencyWaitableObject для получения дескриптора ожидания. Дескриптор ожидания — это указатель на объект ожидания. Сохраните этот дескриптор — он будет использоваться циклом отрисовки.


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


Шаг 4. Ожидание перед отрисовкой каждого кадра

Прежде чем начинать отрисовку каждого кадра, цикл отрисовки должен дождаться сигнала от цепочки буферов, передаваемого через объект ожидания. Это относится и к первому кадру, отрисованному с использованием цепочки буферов. Используйте функцию WaitForSingleObjectEx, предоставляя дескриптор ожидания, полученный в шаге 2, для указания начала каждого кадра.

В следующем примере показан цикл отрисовки из примера 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);
    }
}


В следующем примере показан вызов WaitForSingleObjectEx из примера 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
        );
}


Что должна делать игра во время ожидания представления цепочки буферов?

Если в игре нет никаких задач, пересекающихся с циклом отрисовки, ей следует ожидать представления цепочки буферов, поскольку это помогает сэкономить электроэнергию, что особенно важно на мобильных устройствах. В противном случае можно использовать многопоточность для выполнения задач, пока игра ожидает представления цепочки буферов. Вот примеры задач, которые может выполнять игра:

  • Обработка сетевых событий
  • Обновление искусственного интеллекта
  • Обработка физических эффектов на ЦП
  • Отрисовка с отложенным контекстом (на поддерживаемых устройствах)
  • Загрузка ресурсов

Дополнительные сведения о многопоточном программировании в Windows см. в следующих разделах по теме.

Связанные разделы

Пример DirectXLatency
IDXGISwapChain2::GetFrameLatencyWaitableObject
WaitForSingleObjectEx
Windows.System.Threading
Асинхронное программирование на языке C++
Процессы и потоки
Синхронизация
Использование объектов событий (Windows)

 

 

Показ:
© 2014 Microsoft