2017-05-04 87 views
4

我設置了一個DX12應用程序,它只在每一幀清除backbuffer。DXGI Waitable SwapChain不等待

它確實是準系統:沒有PSO,沒有根... 唯一的特殊性是它在開始一個新幀之前等待swapChain與Present()完成(我設置了幀等待時間1以及只有2個緩衝區)。

第一幀運行良好,但立即開始繪製第二幀,當然,命令分配器會抱怨它在GPU上仍然執行命令時被重置。

我當然可以設置一個圍欄來等待gpu完成,然後再移動到一個新的框架,但我認爲這是等待交換鏈對象的工作。

這裏是渲染程序:這個程序有一個可等待的對象至極我從swapchain得到

if (m_command_allocator->Reset() == E_FAIL) { throw; } 

HRESULT res = S_OK; 
res = m_command_list->Reset(m_command_allocator.Get(), nullptr); 
if (res == E_FAIL || res == E_OUTOFMEMORY) { throw; } 

m_command_list->ResourceBarrier(1, 
&CD3DX12_RESOURCE_BARRIER::Transition(m_render_targets[m_frame_index].Get(), 
D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET)); 

m_command_list->RSSetViewports(1, &m_screen_viewport); 
m_command_list->RSSetScissorRects(1, &m_scissor_rect); 
m_command_list->ClearRenderTargetView(get_rtv_handle(), 
DirectX::Colors::BlueViolet, 0, nullptr); 
m_command_list->OMSetRenderTargets(1, &get_rtv_handle(), true, nullptr); 

m_command_list->ResourceBarrier(1, 
&CD3DX12_RESOURCE_BARRIER::Transition(m_render_targets[m_frame_index].Get(), 
D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT)); 

tools::throw_if_failed(m_command_list->Close()); 
ID3D12CommandList* ppCommandLists[] = { m_command_list.Get() }; 
m_command_queue->ExecuteCommandLists(_countof(ppCommandLists), 
ppCommandLists); 

if (m_swap_chain->Present(1, 0) != S_OK) { throw; } 
m_frame_index = m_swap_chain->GetCurrentBackBufferIndex(); 

我循環:

while (WAIT_OBJECT_0 == WaitForSingleObjectEx(waitable_renderer, INFINITE, TRUE) && m_alive == true) 
{ 
    m_graphics.render(); 
} 

,我和可等待初始化swapchain標記:

DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; 
swap_chain_desc.BufferCount = s_frame_count; 
swap_chain_desc.Width = window_width; 
swap_chain_desc.Height = window_height; 
swap_chain_desc.Format = m_back_buffer_format; 
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; 
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; 
swap_chain_desc.SampleDesc.Count = 1; 
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; 

ComPtr<IDXGISwapChain1> swap_chain; 
tools::throw_if_failed(
    factory->CreateSwapChainForHwnd(m_command_queue.Get(), window_handle, &swap_chain_desc, nullptr, nullptr, &swap_chain)); 

我所說的SetFrameLatency創建swapChain之後:

ComPtr<IDXGISwapChain2> swap_chain2; 
tools::throw_if_failed(m_swap_chain.As(&swap_chain2)); 

tools::throw_if_failed(swap_chain2->SetMaximumFrameLatency(1)); 

m_waitable_renderer = swap_chain2->GetFrameLatencyWaitableObject(); 

而且swapChain調整是去與它:

tools::throw_if_failed(
    m_swap_chain->ResizeBuffers(s_frame_count, window_width, window_height, m_back_buffer_format, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)); 

我的問題是:難道我設置的東西了不正確的?或者這是等待交換鏈工作的方式(也就是說,在等待交換鏈變得可用之前,您還需要用隔離與gpu同步)?

編輯:添加SetFrameLatency呼叫+ C++着色

+0

你的代碼似乎要被罰款,但目前尚不清楚是否確實叫'GetFrameLatencyWaitableObject '獲得'waitable_renderer'。 – VTT

+0

我根據您的建議和評論編輯了我的問題。 –

回答

1

的可等待交換鏈是從保護d3d12對象的工作獨立於被修改或復位,同時仍然在由GPU使用。

可等待的交換鏈允許您將等待從Present中幀的末尾移動到具有可等待對象的幀的開始位置。它具有打擊延遲和對排隊進行更多控制的優勢。

通過對物體進行遮擋,可以查詢GPU是否完成。我建議你不要只是將手指交叉在一個系統上工作一天,它可能不適用於具有不同驅動程序或不同機器的下一個手指。因爲你不想讓每一幀等待GPU完成,你必須創建幾個命令分配器,通常創建一個計數爲min(maxlatency+1,swapchain buffer count),但爲了安全起見,我個人使用back buffer count + 1..3。你將會發現你將會創建更多的分配器來處理多線程。

是什麼意思爲您的代碼:

  1. 具有關聯柵值
  2. 創建一個柵欄(和全球柵欄下一個值)
  3. 交換鏈等待創建一個環形緩衝區幾個分配器
  4. 挑下一個分配器
  5. 如果fence.GetCompletedValue()< allocator.fenceValue然後WaitCompletion
  6. 渲染
  7. 信號與命令隊列圍欄,圍欄值存儲分配和增加
  8. 跳轉到3
+0

在哪些情況下可以完成渲染並顯示幀,但是完成柵欄未被觸發? – VTT

+0

交換鏈等待對象告訴您一個緩衝區已準備好被填充,因爲您只使用一個分配器,所以您沒有假設您嘗試填充的上一個緩衝區,因此您第一次等待將是非操作(因爲2緩衝區,還有一個還沒有使用),但由於gpu仍然可以在第一個上工作,所以不能重置分配器。要重用它,你首先需要在標記渲染結束的柵欄上等待,但因爲你不想序列化cpu和gpu,所以你需要創建幾個分配器。 – galop1n

+0

此外,您可以在dx12中看到更多模式,可以從交換鏈後臺緩衝區中解開您的分配器,如果重新使用bundle,如果您在單獨的隊列中使用異步計算,則... – galop1n