渲染三大金刚——CommandQueue、CommandList、CommandAllocator之间的关系梳理

之前看Vulkan的书,写得云里雾里的,搞到最后只记住了几个API把代码跑起来,好久没用后还忘了,直到最近抽空看DX12的龙书,才彻底把它们的关系给理清楚,特此记录。

命令队列(CommandQueue):在GPU中,是一块环形缓冲区,是GPU的命令执行队列,每个GPU至少维护一个CommandQueue。如果该队列为空,GPU将空闲空转,等到有指令过来;如果该队列满了,将阻塞CPU的执行。

命令列表(CommandList):在CPU中,用来记录GPU的执行指令,我们期望让GPU执行的任务会通过它来记录。

命令分配器(CommandAllocator):在CPU中,用来给CommandList记录的指令分配空间,这个空间用来在CPU侧存储指令,不存储资源。

提交指令:DX12通过ExecuteCommandLists函数,将CommandList中记录的指令提交给GPU中的CommandQueue。CPU和GPU是两个处理器,它们在两条独立的跑道上并行地跑,CommandQueue就是GPU的跑道。CommandList在调用 设置视口(SetViewPort)、清屏(ClearRenderTarget)、发起绘制(DrawIndex) 这些函数的时候,并没有真正地执行这些操作,只是将这些指令记录下来,直到执行ExecuteCommandLists函数,指令就从CPU送到GPU的CommandQueue中,这个从CPU送GPU过程不一定是立即送的,当然GPU也不是立即调用,而是按CommandQueue中的顺序依次执行指令。

三大金刚关系:一个GPU至少维护一个CommandQueue。CommandList用于在CPU侧进行指令记录,CommandList可以有多个。创建CommandList要指定分配器CommandAllocator,CommandAllocator也可以有多个,同时一个CommandAllocator可以关联多个CommandList,但是关联同一CommandAllocator的CommandList们不能同时记录指令,因为CommandList记录的指令的内存由CommandAllocator来分配,我们需要保证(((记录的指令)的内存)的连续性),这样才能一把送到CommandQueue中,并发记录会破坏内存连续性。

关于重置:CommandQueue在GPU中,是GPU的执行跑道,不能重置。CommandList在记录完指令并提交指令后可以重置(然后可以复用它记录新的指令),因为提交指令后,CommandAllocator还在维护着这块内存。在没有确定GPU执行完CommandAllocator中的指令前不能重置CommandAllocator,因为底下实现可能会起Job一点点地去送指令到GPU,而提交指令的函数操作会立即返回。

资源和指令不同,资源是执行指令需要的“原料”,即需要加工处理的数据。资源会开在GPU内存中,由对应的资源描述符来引用,资源有Host可见(CPU、GPU均可访问)的和Host不可见(CPU不可访问,只有GPU可访问)的,通过映射GPU内存地址,可以把内存中的资源数据拷贝到Host可见的GPU内存中。

有一个概念需要明确一下:显卡 ≠ GPU,显卡 = GPU + GPU底板 + GPU独立内存。这个和计算机组成中的CPU、主板、内存条是对应的。

GPU内存分为共享内存和独立内存。独立内存在显卡上,就是常说的显存,共享内存在内存条上。这个共享内存的共享是指和应用程序共享(不是指CPU和GPU共享),其他应用程序也可以用这块内存区域,只是操作系统会优先分给GPU使用。共享内存的大小由操作系统根据内存条的大小、显卡要求等环境参数自动分配,不可更改,独立内存在显卡上,买显卡的时候会标显存大小。Host可见的资源不一定在共享内存中,事实上操作系统会优先使用独立内存(物理上距离GPU也近,传输距离短),但是在加载了大量资源后,独立内存不够存放了,就会使用共享内存。


本文为原创内容,遵循CC BY-ND 4.0协议,署名-禁止演绎。
转载请注明出处:https://tis.ac.cn/blog/kongdeyou/commandqueue_commandlist_commandallocator/

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注