DirectX 12 更新描述符堆
Posted
技术标签:
【中文标题】DirectX 12 更新描述符堆【英文标题】:DirectX 12 Updating the Descriptor Heap 【发布时间】:2017-01-15 03:14:23 【问题描述】:我目前正在为 DirectX12 编写自己的图形框架(我已经为个人游戏引擎编写了几个 DirectX 11 框架),我目前正在 trying to copy the methods used in the recent Hitman game 进行资源绑定。
我对处理 SRV/CBV/UAV 堆的每个对象资源绑定的最佳方式感到困惑。我看过几个 GDC 演示文稿,它们似乎都掩盖了这一点。
一次只能绑定 1 个 SRV/CBV/UAV 堆,并且在命令列表中间切换当前绑定的堆可能会因强制刷新而对某些硬件的性能不利。因此,使用新描述符更新堆的最佳方法是什么?对我来说,似乎每个命令列表都会:
-
为自己获取 SRV/CBV/UAV 堆。
对于对象子集中的每个对象,在堆上创建描述符,指向放置在单独上传堆中的每个对象数据。
之后,另一个命令列表获取这个填充的描述符堆并将其绑定,然后发出与
SetGraphicsRootDescriptorTable
混合的绘图调用,以便在当前描述符堆中移动。
话虽如此,网上的一些消息来源 (including another SO post) 建议使用一个大型 SRV/CBV/UAV 堆并使用 CPU 可见的堆复制到其中。我假设他们没有尝试使用异步CopyDescriptors
,而是CopyBufferRegion
。我尝试使用CopyBufferRegion
来更新每个对象的数据,但在我看来,D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER
和D3D12_RESOURCE_STATE_COPY_DEST
之间有如此多的转换,这似乎表现不佳。我是不是误会了什么?任何澄清将不胜感激。
【问题讨论】:
请务必查看MiniEngine 和DirectX Tool Kit for DirectX 12 以获取处理此问题的方法的一些示例代码。 【参考方案1】:CopyDescriptors
不是异步的,它是在 CPU 上立即执行的 CPU 操作。它可以在为 volatile 描述符执行命令列表之前的任何时间发生(在使用它的命令列表操作被记录之后),或者必须准备好用于静态描述符(根签名 1.1)。
通常的方法是拥有一个大的描述符堆,保留一部分用于静态描述符,然后将其余部分用作环形缓冲区,按需分配描述符表偏移量以复制并使用所需的描述符进行任何绘制/计算操作。
CopyBufferRegion
与这里无关,请记住映射缓冲区也是一个即时操作,因此您还为每个对象常量缓冲区环形缓冲了一大块内存,然后循环进入它。唯一的问题是,您需要确保在它们可能仍在使用时不会覆盖内存或描述符,因此您必须设置围栏以防止这种情况发生。
【讨论】:
那么如果我使用CopyDescriptors
,它什么时候更新GPU端的堆?当我打电话给SetGraphicsRootDescriptorTable
?
不,CopyDescriptors 是即时的,一旦返回,复制就完成了。唯一棘手的是根签名 1.0 与 1.1。 1.0 和 volatile table 不假定描述符在您在 CPU 上使用它们时已准备好,它们假定当您关闭命令列表时它们将在此处。带有静态表的 1.1 假定描述符在您设置它们以触发平局时已准备就绪,并且此后它们不会更改。
因此,如果我要渲染多个对象,每个对象都具有唯一的 cbuffer 数据,我会调用 CopyDescriptors
以在每个对象的不同偏移量处更新描述符堆?
是的,确切地说,你将堆作为一个大的环形缓冲区走动,根据渲染操作的需求分配表,因为描述符必须存在,直到 gpu 完成它们。
CopyDescriptors
上的 CPU 停顿似乎非常昂贵,每个对象打开/关闭到 GPU 的管道一次。有什么我想念的吗?以上是关于DirectX 12 更新描述符堆的主要内容,如果未能解决你的问题,请参考以下文章
DirectX 12 应用程序在 Windows 11 中崩溃
DirectX 12和DirectX 11选哪个,有什么区别