我开始学习 Direct3D 12 并且难以理解 CPU-GPU 同步。据我了解,栅栏(ID3D12Fence)不过是用作计数器的 UINT64(无符号长长)值。但它的方法让我感到困惑。以下是 D3D12 示例的部分源代码。( https://github.com/d3dcoder/d3d12book )
void D3DApp::FlushCommandQueue()
{
// Advance the fence value to mark commands up to this fence point.
mCurrentFence++;
// Add an instruction to the command queue to set a new fence point. Because we
// are on the GPU timeline, the new fence point won't be set until the GPU finishes
// processing all the commands prior to this Signal().
ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));
// Wait until the GPU has completed commands up to this fence point.
if(mFence->GetCompletedValue() < mCurrentFence)
{
HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
// Fire event when GPU hits current fence.
ThrowIfFailed(mFence->SetEventOnCompletion(mCurrentFence, eventHandle));
// Wait until the GPU hits current fence event is fired.
WaitForSingleObject(eventHandle, INFINITE);
CloseHandle(eventHandle);
}
}
据我了解,这部分正在尝试“刷新”命令队列,这基本上是让 CPU 等待 GPU 直到达到给定的“围栏值”,以便 CPU 和 GPU 具有相同的围栏值。
问:如果这个 Signal() 是一个让 GPU 更新给定 ID3D12Fence 内的栅栏值的函数,为什么需要那个 mCurrentFence 值?
根据 Microsoft Doc,它说“将栅栏更新为指定值”。什么指定值?我需要的是“获取最后完成的命令列表值”,而不是设置或指定。这个指定的值是干什么用的?
对我来说,它似乎必须像
// Suppose mCurrentFence is 1 after submitting 1 command list (Index 0), and the thread reached to here for the FIRST time
ThrowIfFailed(mCommandQueue->Signal(mFence.Get()));
// At this point Fence value inside mFence is updated
if (m_Fence->GetCompletedValue() < mCurrentFence)
{
...
}
如果 m_Fence->GetCompletedValue() 为 0,
如果 (0 < 1)
GPU 还没有操作命令列表(索引 0),那么 CPU 必须等到 GPU 跟进。那么调用 SetEventOnCompletion、WaitForSingleObject 等就有意义了。
如果 (1 < 1)
GPU 已完成命令列表(索引 0),因此 CPU 无需等待。
在执行命令列表的地方增加 mCurrentFence。
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
mCurrentFence++;