DXGI 1.3 스왑 체인으로 대기 시간 단축
DXGI 1.3을 사용하여 스왑 체인이 새 프레임 렌더링을 시작하는 적절한 시간을 알릴 때까지 대기하여 유효 프레임 대기 시간을 줄입니다. 게임은 일반적으로 플레이어 입력이 수신된 시간부터 디스플레이를 업데이트하여 게임이 해당 입력에 응답하는 시점까지 가능한 한 가장 짧은 대기 시간을 제공해야 합니다. 이 항목에서는 게임에서 유효 프레임 대기 시간을 최소화하는 데 사용할 수 있는 Direct3D 11.2부터 사용할 수 있는 기술에 대해 설명합니다.
백 버퍼 대기 시 대기 시간이 어떻게 감소하나요?
대칭 이동 모델 스왑 체인을 사용하면 게임에서 IDXGISwapChain::Present를 호출할 때마다 백 버퍼 '플립'이 큐에 대기됩니다. 렌더링 루프가 Present()를 호출하면 시스템은 이전 프레임을 표시할 때까지 스레드를 차단하므로 실제로 표시되기 전에 새 프레임을 큐에 대기할 수 있는 공간을 만듭니다. 이로 인해 게임이 프레임을 그리는 시간과 시스템에서 해당 프레임을 표시할 수 있는 시간 사이에 추가 대기 시간이 발생합니다. 시스템은 게임이 렌더링되는 시간과 각 프레임을 표시하는 시간 사이에 거의 전체 추가 프레임을 항상 대기하는 안정적인 평형에 도달하는 경우가 대부분입니다. 시스템이 새 프레임을 수락할 준비가 될 때까지 대기한 뒤 현재 데이터를 기반으로 프레임을 렌더링하고 프레임을 즉시 큐에 추가하는 것이 좋습니다.
DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT 플래그를 사용하여 대기 가능 스왑 체인을 만듭니다. 이러한 방식으로 생성한 스왑 체인은 시스템이 실제로 새 프레임을 수락할 준비가 되면 렌더링 루프에 알릴 수 있습니다. 이렇게 하면 게임이 현재 데이터를 기반으로 렌더링한 다음 결과를 현재 큐에 바로 배치할 수 있습니다.
1단계. 대기 가능 스왑 체인 만들기
CreateSwapChainForCoreWindow를 호출할 때 DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT 플래그를 지정합니다.
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단계. 프레임 지연 설정
IDXGIDevice1::SetMaximumFrameLatency를 호출하는 대신 IDXGISwapChain2::SetMaximumFrameLatency API를 사용하여 프레임 대기 시간을 설정합니다.
기본적으로 대기 가능한 스왑 체인의 프레임 대기 시간은 1로 설정되므로 대기 시간이 가장 짧아지지만 CPU-GPU 병렬 처리도 줄어듭니다. 60FPS를 달성하기 위해 CPU-GPU 병렬 처리가 증가해야 하는 경우, 즉 CPU와 GPU가 각각 프레임 처리 렌더링 작업에 16.7ms 미만을 소비하지만 결합 합계가 16.7ms보다 큰 경우 프레임 대기 시간을 2로 설정합니다. 이렇게 하면 GPU가 이전 프레임 동안 CPU에 의해 대기 중인 작업을 처리하면서 CPU가 현재 프레임에 대한 렌더링 명령을 독립적으로 제출할 수 있습니다.
// 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);
}
}
다음 예제에서는 DirectXLatency 샘플의 WaitForSingleObjectEx 호출을 보여 줍니다.
// 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
);
}
내 게임은 스왑 체인이 표시되기를 기다리는 동안 무엇을 해야 하나요?
게임에 렌더링 루프를 차단하는 작업이 없는 경우, 스왑 체인이 표시될 때까지 기다리게 하면 절전되므로 유리할 수 있으며, 이는 모바일 디바이스에서 특히 중요합니다. 그렇지 않으면 게임이 스왑 체인이 표시되도록 기다리는 동안 다중 스레딩을 사용하여 작업을 수행할 수 있습니다. 다음은 게임에서 완료할 수 있는 몇 가지 작업입니다.
- 네트워크 이벤트 처리
- AI 업데이트
- CPU 기반 물리학
- 지연된 컨텍스트 렌더링(지원되는 디바이스상에서)
- 자산 로드
Windows의 다중 스레드 프로그래밍에 대한 자세한 내용은 다음 관련 항목을 참조하십시오.