DX12 命令队列和栅栏
在正确的时间同步 CPU 和 GPU
该博客上撰写的文章是出于教学目的而创建的。 另外,文章中的信息可能不正确,请谨慎。
在 DirectX 12 中,CPU 记录命令列表,GPU 稍后执行。 由于两个设备同时移动,因此它们必须同步,以便 CPU 不会覆盖 GPU 仍在使用的资源。
命令队列和命令列表
Command List是记录绘图命令的列表,Command Queue将完成的列表提交给GPU。
CPU
命令分配器初始化
命令列表历史记录
CommandList Close
↓
提交命令队列
↓
GPU执行
CommandAllocator 管理录制所需的内存。 当 GPU 仍在读取前一帧的指令时,不应重置同一个分配器。
使用 Fence 检查完成时间
void WaitForGPU()
{
const UINT64 SignalValue = ++FenceValue;
CommandQueue->Signal(Fence, SignalValue);
if (Fence->GetCompletedValue() < SignalValue)
{
Fence->SetEventOnCompletion(SignalValue, FenceEvent);
WaitForSingleObject(FenceEvent, INFINITE);
}
}
当信号值进入队列时,GPU 会处理之前的所有命令,然后更新 Fence 值。 仅当完成值小于目标时,CPU 才会等待事件。
如果你等待每一帧,那么速度会很慢。
在三角形的例子中,为了更容易理解,WaitForGPU()可以调用。 但是,如果这样做,CPU 和 GPU 就无法协同工作并在每一帧串行运行。
在实际应用中,BackBuffer的个数就有多少个Frame Resources,并且单独记录每帧的Fence值。
struct FrameContext
{
ComPtr<ID3D12CommandAllocator> Allocator;
UINT64 FenceValue = 0;
};
它只等待当前使用的帧资源的栅栏,并允许GPU继续处理其他帧。
打开调试层
在 DX12 中,开发人员对错误的状态转换或描述符的使用负有直接责任。 如果在初始化之前打开调试层,您可以更快地找出哪些资源状态和命令顺序错误。
DX12同步的目的不是无条件等待。 它允许CPU和GPU尽可能同时工作,同时精确地等待再次使用相同的资源。