前言
本篇将展示如何使用篱笆和多个分配器将多个帧排队到 GPU,也就是frame buffering。本篇基于渲染三角形篇展示新增步骤
在这之前我们使用一个命令分配器来记录交换链中两个缓冲区的绘制命令,这意味着我们需要在记录命令创建新帧之前刷新命令队列(因为所有命令都记录在相同的内存空间中,我们不能覆盖 GPU 仍在使用的命令),CPU和GPU并没有做到并行,很显然这样的做法效率并不高。因此在本篇中,将尝试为交换链中的每个缓冲区使用单独的命令分配器来解决这个问题
流程图
header file
ComPtr<ID3D12CommandAllocator> m_commandAllocators[FrameCount];
UINT64 m_fenceValues[FrameCount];
source file
加载管线
创建命令分配器
与先前不同,由于我们需要使用多个分配器,因此将创建分配器的步骤放在创建帧资源的循环内,如此每个帧资源都有一个分配器
// Create frame resources.
{
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart());
// Create a RTV and a command allocator for each frame.
for (UINT n = 0; n < FrameCount; n++)
{
ThrowIfFailed(m_swapChain->GetBuffer(n, IID_PPV_ARGS(&m_renderTargets[n])));
m_device->CreateRenderTargetView(m_renderTargets[n].Get(), nullptr, rtvHandle);
rtvHandle.Offset(1, m_rtvDescriptorSize);
ThrowIfFailed(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[n])));
}
}
加载资源
创建围栏
ThrowIfFailed(m_device->CreateFence(m_fenceValues[m_frameIndex], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
m_fenceValues[m_frameIndex]++;
等待GPU
// Wait for pending GPU work to complete.
void D3D12HelloFrameBuffering::WaitForGpu()
{
// Schedule a Signal command in the queue.
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), m_fenceValues[m_frameIndex]));
// Wait until the fence has been processed.
ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent));
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
// Increment the fence value for the current frame.
m_fenceValues[m_frameIndex]++;
}
渲染
移至下一帧
我们摒弃先前的在渲染过程中让CPU等待GPU渲染完成后才加入新命令的方式,而是在渲染中前往下一帧,提前加入新命令
在首次调用MoveToNextFrame()后,currentFenceValue为2,m_frameIndex为1,由于在调用GetCurrentBackBufferIndex()后m_fenceValues[m_frameIndex]的值为0,因此现在不会进入if块,随后index 0的m_fenceValues更新为3。在下一次调用中,currentFenceValue为3,前一帧的围栏值为2,只有当GetCompletedValue()返回的值至少为2时,才能确定GPU完成了前一帧的绘制(注:本例中后台缓冲区的个数为2个,会不断复用)
// Prepare to render the next frame.
void D3D12HelloFrameBuffering::MoveToNextFrame()
{
// Schedule a Signal command in the queue.
const UINT64 currentFenceValue = m_fenceValues[m_frameIndex];
ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), currentFenceValue));
// Update the frame index
// 获取下一帧的索引
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
// If the next frame is not ready to be rendered yet, wait until it is ready
if (m_fence->GetCompletedValue() < m_fenceValues[m_frameIndex])
{
ThrowIfFailed(m_fence->SetEventOnCompletion(m_fenceValues[m_frameIndex], m_fenceEvent));
WaitForSingleObjectEx(m_fenceEvent, INFINITE, FALSE);
}
// Set the fence value for the next frame.
m_fenceValues[m_frameIndex] = currentFenceValue + 1;
}
reference
https://paminerva.github.io/docs/LearnDirectX/01.F-Hello-Frame-Buffering.html
https://github.com/microsoft/DirectX-Graphics-Samples
标签:ThrowIfFailed,分配器,fence,frame,fenceValues,如何,DX,FrameBuffer,frameIndex From: https://www.cnblogs.com/chenglixue/p/17127893.html