我目前正在为DirectX12编写我自己的图形框架(我已经为个人游戏引擎编写了几个DirectX 11框架),并且我目前正在尝试复制最近的《杀手》游戏中用于资源绑定的方法。
我对处理SRV/CBV/UAV堆的每个对象资源绑定的最佳方式感到困惑。我看过几次GDC演示,它们似乎都掩盖了这一点。
一次只能绑定1个SRV/CBV/UAV堆,在命令列表中间切换当前绑定的堆可能会强制刷新,从而对某些硬件的性能造成不利影响。因此,用新描述符更新堆的最佳方法是什么?对我来说,似乎每个命令列表都会:
- 为自己获取一个SRV/CBV/UAV堆
- 对于对象子集中的每个对象,在堆上创建描述符,指向放置在单独上传堆中的每对象数据
- 然后,另一个命令列表获取这个填充的描述符堆并绑定它,然后发出与
SetGraphicsRootDescriptorTable
混合的draw调用,以便在当前描述符堆中移动
也就是说,一些在线消息来源(包括另一篇SO帖子)建议使用一个大型SRV/CBV/UAV堆,并使用CPU可见堆将其复制到其中。我假设他们没有尝试使用异步CopyDescriptors
,而是使用CopyBufferRegion
。我尝试使用CopyBufferRegion
来更新每个对象的数据,但对我来说,由于D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER
和D3D12_RESOURCE_STATE_COPY_DEST
之间有太多转换,这似乎性能不佳。我是不是误解了什么?如有任何澄清,我们将不胜感激。
CopyDescriptors
不是异步的,它是在CPU上立即执行的CPU操作。它可以在为易失性描述符执行命令列表之前的任何时候发生(在记录使用它的命令列表操作之后),或者必须在使用静态描述符时做好准备(根签名1.1)。
通常的方法是有一个大的描述符堆,保留一部分用于静态描述符,然后将其余部分用作环形缓冲区,根据需要分配描述符表偏移量以进行复制,并将所需的描述符用于任何绘制/计算操作。
CopyBufferRegion
与此无关,请记住,映射缓冲区也是一个即时操作,因此您还可以为每个对象的常量缓冲区环形缓冲一大块内存,然后循环使用它。唯一的问题是,您需要确保在内存或描述符可能仍在使用时不会覆盖它们,因此必须进行隔离以防止这种情况发生。