Yoo*_* OH 5 directx synchronization gpu direct3d12
我开始学习 Direct3D 12,但难以理解 CPU-GPU 同步。据我了解,fence(ID3D12Fence)只不过是用作计数器的UINT64(unsigned long long)值。但它的方法让我困惑。以下是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);
}
}
Run Code Online (Sandbox Code Playgroud)
据我了解,这部分试图“刷新”命令队列,这基本上是让 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)
{
...
}
Run Code Online (Sandbox Code Playgroud)
如果 m_Fence->GetCompletedValue() 为 0,
如果(0 < 1)
GPU尚未操作命令列表(索引0),则CPU必须等待GPU跟进。那么调用 SetEventOnCompletion、WaitForSingleObject 等就有意义了。
如果 (1 < 1)
GPU 已完成命令列表(索引 0),因此 CPU 无需等待。
在执行命令列表的地方增加 mCurrentFence 。
mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);
mCurrentFence++;
Run Code Online (Sandbox Code Playgroud)
小智 4
mCommandQueue->Signal(mFence.Get(), mCurrentFence)
mCurrentFence
一旦执行了命令队列中所有先前排队的命令,就将栅栏值设置为。在这种情况下,“指定值”是mCurrentFence。
当您开始时,fence 和 mCurrentFence 的值都设置为 0。接下来,mCurrentFence 设置为 1。然后,mCommandQueue->Signal(mFence.Get(), 1)
一旦该队列上的所有内容执行完毕,我们就会将 fence 设置为 1。最后我们mFence->SetEventOnCompletion(1, eventHandle)
依次WaitForSingleObject
调用等待栅栏设置为 1。
将 1 替换为 2 以进行下一次迭代,依此类推。
请注意,这mCommandQueue->Signal
是一个非阻塞操作,不会立即设置栅栏的值,只有在执行完所有其他 GPU 命令后才设置。您可以假设m_Fence->GetCompletedValue() < mCurrentFence
在此示例中始终如此。
为什么需要 mCurrentFence 值?
我想这不是必需的,但是您可以通过这种方式跟踪栅栏值来避免额外的 API 调用。在这种情况下你也可以这样做:
// retrieve last value of the fence and increment by one (Additional API call)
auto nextFence = mFence->GetCompletedValue() + 1;
ThrowIfFailed(mCommandQueue->Signal(mFence.Get(), nextFence));
// Wait until the GPU has completed commands up to this fence point.
if(mFence->GetCompletedValue() < nextFence)
{
HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
ThrowIfFailed(mFence->SetEventOnCompletion(nextFence, eventHandle));
WaitForSingleObject(eventHandle, INFINITE);
CloseHandle(eventHandle);
}
Run Code Online (Sandbox Code Playgroud)