Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Используйте DXGI 1.3, чтобы уменьшить эффективную задержку кадров, ожидая, когда цепочка обмена подаст сигнал на начало рендеринга нового кадра. Игры обычно должны обеспечить наименьший объем задержки с момента получения входных данных игрока до того, как игра реагирует на эти входные данные, обновляя дисплей. В этом разделе описывается способ, доступный начиная с Direct3D 11.2, который можно использовать для минимизации эффективной задержки кадров в игре.
Как ожидание обратного буфера уменьшает задержку?
При использовании цепочки обмена в модели с переворотом, перевороты обратного буфера помещаются в очередь всякий раз, когда игра вызывает IDXGISwapChain::Present. Когда цикл отрисовки вызывает Present(), система блокирует поток до тех пор, пока не завершится вывод предыдущего кадра, освобождая место для постановки в очередь нового кадра перед его фактическим выводом. Это приводит к дополнительной задержке между временем, когда игра рисует кадр и время, когда система позволяет отображать этот кадр. Во многих случаях система достигнет устойчивого равновесия, где игра всегда ждёт почти полный дополнительный кадр между отрисовкой и моментом показа каждого кадра. Лучше ждать, пока система будет готова принять новый кадр, а затем отрисовать кадр на основе текущих данных и немедленно поставить его в очередь.
Создайте ожидаемую цепочку обмена с флагом DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT. Цепочки буферов, созданные таким образом, могут уведомить цикл отрисовки, когда система действительно готова принять новый кадр. Это позволяет игре отображаться на основе текущих данных, а затем сразу поместить результат в текущую очередь.
Шаг 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, что приводит к минимальной задержке, но также уменьшает CPU-GPU параллелизм. Если требуется увеличить CPU-GPU параллелизм для достижения 60 FPS – то есть, если процессор и GPU тратят на отрисовку кадра менее 16,7 мс каждый, но их совокупное время больше 16,7 мс — установите задержку кадра на 2. Это позволяет GPU обрабатывать подготовленные ЦП задачи с предыдущего кадра, одновременно позволяя ЦП независимо отправлять команды на отрисовку для текущего кадра.
// 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 см. в следующих связанных разделах.