我很难在 DX12 中接受多线程渲染的概念。
根据 MSDN,必须将绘制命令写入直接命令列表(最好使用捆绑包),然后将这些列表提交到命令队列。也有人说直接命令列表可以有多个命令队列。但我不清楚这样做的目的是什么。
我通过在并行线程中构建命令列表来充分利用多线程,不是吗?如果是这样,我为什么要与设备关联多个命令队列?
我怀疑命令队列管理不当会导致渲染库开发后期的性能出现巨大问题。
我很难在 DX12 中接受多线程渲染的概念。
根据 MSDN,必须将绘制命令写入直接命令列表(最好使用捆绑包),然后将这些列表提交到命令队列。也有人说直接命令列表可以有多个命令队列。但我不清楚这样做的目的是什么。
我通过在并行线程中构建命令列表来充分利用多线程,不是吗?如果是这样,我为什么要与设备关联多个命令队列?
我怀疑命令队列管理不当会导致渲染库开发后期的性能出现巨大问题。
directx 12 的主要好处是命令的执行几乎是纯异步的。这意味着当您调用ID3D12CommandQueue::ExecuteCommandLists
它时,它将启动传入的命令的工作。然而,这带来了另一点。一个常见的误解是渲染现在以某种方式是多线程的,而这根本不是真的。所有工作仍然在 GPU 上执行。然而,命令列表记录是在多个线程上完成的,因为您将为每个需要它的线程创建一个 ID3D12GraphicsCommandList 对象。
一个例子:
DrawObject DrawObjects[10];
ID3D12CommandQueue* GCommandQueue = ...
void RenderThread1()
{
ID3D12GraphicsCommandList* clForThread1 = ...
for (int i = 0; i < 5; i++)
clForThread1->RecordDraw(DrawObjects[i]);
}
void RenderThread2()
{
ID3D12GraphicsCommandList* clForThread2 = ...
for (int i = 5; i < 10; i++)
clForThread2->RecordDraw(DrawObjects[i]);
}
void ExecuteCommands()
{
ID3D12GraphicsCommandList* cl[2] = { clForThread1, clForThread2 };
GCommandQueue->ExecuteCommandLists(2, cl);
GCommandQueue->Signal(...)
}
这个例子是一个非常粗略的用例,但这是一般的想法。您可以在不同的线程上记录场景的对象,以消除记录命令的 CPU 开销。
然而,另一个有用的事情是,通过此设置,您可以启动渲染任务并开始录制另一个任务。
一个例子
void Render()
{
ID3D12GraphicsCommandList* cl = ...
cl->DrawObjectsInTheScene(...);
CommandQueue->Execute(cl); // Just send it to the gpu to start rendering all the objects in the scene
// And since we have started the gpu work on rendering the scene, we can move to render our post processing while the scene is being rendered on the gpu
ID3D12GraphicsCommandList* cl2 = ...
cl2->SetBloomPipelineState(...);
cl2->SetResources(...);
cl2->DrawOnScreenQuad();
}
与 directx 11 或 opengl 相比,这里的优势在于这些 api 可能只是坐在那里进行记录和记录,并且可能在调用 Present() 之前不会发送它们的命令,这会迫使 cpu 等待,并产生开销。