我有一个后处理管道,它使用计算着色器处理纹理并将其写入RWByteAddressBuffer。
然后,RWByteAddressBuffer的内容通过直接存储器访问(AMD DirectGMA技术)发送到FPGA器件。意思是,我启动一个外部设备来访问这个缓冲区的物理字节,而不需要Direct3D api知道它。
这是代码的本质:
_context->CSSetShaderResources(0,1,_nonMsaaSrv.GetAddressOf());
_context->CSSetUnorderedAccessViews(0, 1, _unorderedAccessView.GetAddressOf(),nullptr);
_context->CSSetShader(_converter.Get(),0,0);
_context->Dispatch(1920, 1200, 1);
// ... wait for direct3d compute shader to finish processing?
// send the bytes to the fpga:
_dmaController->StartDMA(_d3dBufferPhysicalAddress, fpgaLogicalAddress);
一切正常,但问题是我找不到阻止线程的方法或者获得一个事件来指示计算着色器在GPU上完成其工作。
This question建议使用ID3D11Query进行某种轮询的解决方案。但我的理解是,这只是一个忙碌的等待。我希望找到一个更好的解决方案,可能允许线程通过等待某种事件来阻止。使用Cuda / OpenCL等API非常简单。
那么是否可以在direct3D 11中对计算着色器进行阻塞等待?如果是这样的话?
如果不需要支持Windows 7/8,则可以使用Windows 10 v1703及更高版本上提供的更新接口ID3D11Device5,ID3D11DeviceContext4和ID3D11Fence来实现此目的。
创建fence对象:
HR(_d3dDevice->CreateFence(0, D3D11_FENCE_FLAG_NONE, __uuidof(ID3D11Fence), reinterpret_cast<void**>(_syncFence.GetAddressOf())));
在处理循环中,我们调度计算着色器,并在其后面用递增的计数器对信号进行入队:
++_syncCounter;
_context->Dispatch(1920, 1200, 1);
HR(_context->Signal(_syncFence.Get(), _syncCounter));
HR(_syncFence->SetEventOnCompletion(_syncCounter,_syncEvent.get()));
//等待事件(可能在不同的线程上)
_syncEvent.wait(); // WaitForSingleObject
例如(对于Direct3D12)可以找到here。
ID3D11Query
是你正在寻找的机制;在Direct3D 11中没有任何基于事件的事件。它是一种轮询机制,但与CPU上的正常忙等待不同。
您可以随时对其进行分析以查看它添加的负载,特别是如果您添加延迟以在不同的时间间隔(10毫秒,100毫秒等)检查query->GetData
,以查看您的性能是否有所改善。