如何在DirectX / Direct3D 12中使用fence同步CPU和GPU?

问题描述 投票:0回答:1

我开始学习Direct3D 12,并且在理解CPU-GPU同步方面有困难。据我了解,fence(ID3D12Fence)只不过是用作计数器的UINT64(无符号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);
    }
}

据我所知,这部分试图“清除”命令队列,这基本上是使CPU等待GPU,直到达到给定的“ Fence值”,以便CPU和GPU具有相同的篱笆值。

Q。如果此Signal()是让GPU更新给定ID3D12Fence内的fence值的函数,为什么需要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++;
synchronization gpu directx direct3d12
1个回答
0
投票

mCommandQueue->Signal(mFence.Get(), mCurrentFence)将命令队列中所有先前排队的命令执行完毕后立即将fence值设置为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命令之后,才立即设置fence的值。您可以假设在此示例中,m_Fence->GetCompletedValue() < mCurrentFence始终为true。

为什么需要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);
}
© www.soinside.com 2019 - 2024. All rights reserved.