Directx DXGI可等待的交换链未等待

Directx DXGI可等待的交换链未等待,directx,dxgi,directx-12,Directx,Dxgi,Directx 12,我设置了一个DX12应用程序,它只在每一帧清除backbuffer 这真的是赤裸裸的:没有PSO,没有根。。。 唯一的特殊性是,它在启动新帧()之前等待swapChain使用Present()完成(我也将帧延迟设置为1,并且on上只有2个缓冲区) 第一个帧工作正常,但它立即开始绘制第二个帧,当然,命令分配器会抱怨它在GPU上执行命令时被重置 当然,我可以在移动到新帧之前设置一个围栏,等待gpu完成,但我认为这是可等待交换链对象的工作 以下是渲染例程: if(m_命令\u分配器->重置()==E_

我设置了一个DX12应用程序,它只在每一帧清除backbuffer

这真的是赤裸裸的:没有PSO,没有根。。。 唯一的特殊性是,它在启动新帧()之前等待swapChain使用Present()完成(我也将帧延迟设置为1,并且on上只有2个缓冲区)

第一个帧工作正常,但它立即开始绘制第二个帧,当然,命令分配器会抱怨它在GPU上执行命令时被重置

当然,我可以在移动到新帧之前设置一个围栏,等待gpu完成,但我认为这是可等待交换链对象的工作

以下是渲染例程:

if(m_命令\u分配器->重置()==E_失败){throw;}
HRESULT res=S_正常;
res=m_command_list->Reset(m_command_分配器.Get(),nullptr);
如果(res==E_FAIL | | res==E_OUTOFMEMORY){throw;}
m_命令\u列表->资源屏障(1,
&CD3DX12_资源_屏障::转换(m_呈现_目标[m_框架_索引])。获取(),
D3D12_资源_状态_存在,D3D12_资源_状态_呈现_目标);
m_命令\u列表->RSSETViewport(1,&m_屏幕\u视口);
m_命令\u列表->RSSetScissorRects(1,&m_剪刀\u rect);
m_命令\u列表->ClearRenderTargetView(获取\u rtv\u句柄(),
DirectX::Colors::蓝紫色,0,空PTR);
m_command_list->OMSetRenderTargets(1,&get_rtv_handle(),true,nullptr);
m_命令\u列表->资源屏障(1,
&CD3DX12_资源_屏障::转换(m_呈现_目标[m_框架_索引])。获取(),
D3D12_资源_状态_呈现_目标,D3D12_资源_状态_存在);
工具::失败时抛出(m_命令\u列表->关闭());
ID3D12CommandList*ppCommandList[]={m_command_list.Get()};
m_命令\u队列->执行命令列表(_countof(ppcommandlist),
命令列表);
如果(m_swap_chain->Present(1,0)!=S_OK){throw;}
m_frame_index=m_swap_chain->GetCurrentBackufferIndex();
我使用从swapchain获得的可等待对象循环此例程:

while(WAIT_OBJECT_0==WaitForSingleObjectEx(waitable_渲染器,无限,TRUE)和&m_alive==TRUE)
{
m_graphics.render();
}
我用waitable标志初始化了swapchain:

DXGI_SWAP_CHAIN_DESC1 SWAP_CHAIN_desc={};
交换链描述缓冲区计数=帧计数;
交换链描述宽度=窗口宽度;
交换链描述高度=窗高度;
swap_chain_desc.Format=m_back_buffer_Format;
swap_chain_desc.BufferUsage=DXGI_USAGE_RENDER_TARGET_输出;
交换\u链\u描述.SwapEffect=DXGI\u交换\u效果\u翻转\u丢弃;
swap_chain_desc.SampleDesc.Count=1;
swap_chain_desc.Flags=DXGI_swap_chain_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
ComPtr交换链;
工具::如果失败,则抛出(
factory->CreateSwapChainForHwnd(m_命令\u队列.Get()、窗口\u句柄和交换链\u描述、nullptr、nullptr和交换链));
我在创建交换链后立即调用SetFrameLatency:

ComPtr-swap\u-chain2;
工具::如果失败则抛出(m_swap_chain.As(&swap_chain2));
工具::如果失败则抛出(交换\u链2->SetMaximumFrameLatency(1));
m_waitable_renderer=swap_chain2->GetFrameLatencyWaitableObject();
以及随之产生的交换链调整大小:

tools::如果失败则抛出(
m_交换链->调整缓冲区大小(s_帧计数、窗口宽度、窗口高度、m_后退缓冲区格式、DXGI_交换链标志、帧延迟、可等待对象);
我的问题是:我的设置是否有误?或者这就是等待交换链的工作方式(即在等待交换链可用之前,您还需要使用fences与gpu同步)


< >编辑:添加SETFRAMELATION调用+C++着色< /P> < P>可交换链与D3D12对象的保护工作无关,在GPU使用时仍需修改或重置。 可等待交换链允许您使用可等待对象将等待从当前帧的结尾移动到帧的开头。它的优点是可以抵抗延迟,并对排队进行更多的控制

对象上的围栏允许您查询GPU以完成。我建议你不要只是交叉手指,就好像它在一个系统上工作一样,它在下一个系统上可能无法在不同的驱动程序或不同的机器上工作

因为您不希望每个帧等待GPU完成,所以必须创建多个命令分配器,通常创建
min(maxlatency+1,swapchain buffer count)
,但为了安全起见,我个人使用
后缓冲区计数+1..3
。稍后您会发现,您将创建更多的分配器来处理多线程

这对您的代码意味着什么:

  • 在环形缓冲区中创建多个具有关联围栏值的分配器
  • 创建围栏(和全局围栏下一个值)
  • 交换链等待
  • 选择下一个分配器
  • 如果fence.GetCompletedValue()
  • 渲染
  • 用命令队列向围栏发送信号,将围栏值存储到分配器并递增
  • 跳到3

  • 您的代码似乎很好,虽然不清楚您是否真的调用了
    GetFrameLatencyWaitableObject
    来获取
    waitable\u渲染器
    。我根据您的建议和评论用编辑了我的问题。在什么情况下可以完成渲染并显示帧,但完成栅栏不触发?交换链等待对象会告诉您缓冲区已准备好填充,您对之前尝试填充的缓冲区没有任何假设,因为您仅使用一个分配器,第一次等待将是非操作(因为两个缓冲区和一个缓冲区尚未使用),但由于gpu可能仍在第一个缓冲区上工作,因此无法重置分配器。要重用它,您首先需要在标记渲染结束的围栏上等待,但由于您不想序列化cpu和gpu,因此需要创建多个分配器。此外,在dx12中,您可以看到更多的模式可以将分配器从交换链上解绑回来