在渲染处理的过程中,GPU可能会对资源进行读和写两种操作,在发出绘制命令之前,我们需要将与本次绘制调用(draw call)相关的资源绑定(bind或称链接,link)到渲染流水线上。部分资源可能在每次绘制调用的时候都有所变化,所以我们就每次更新绑定。但是,GPU资源并非直接与渲染流水线进行绑定的,而是通过一种名叫描述符(descriptor)的对象对它简介引用,我们可以把描述符是为一种对送往GPU的资源进行描述的轻量型结构。从本质上来讲,它实际上即为一个中间层;若指定了资源描述符,GPU将能获取实际的资源数据,也能了解到资源的必要信息。因此,我们将把绘制调用的资源,通过指定描述符的方式绑定到渲染流水线。
GPU对资源的读和写
读:例如,从描述物体表面样貌的纹理或者存有3D场景中几何体位置信息的缓冲区中读取数据。
写:例如,向后台缓冲区或深度/模板缓冲区写入数据。
为什么我们额外使用描述符这个中间层呢?究其原因,GPU资源实质上都是一些普通的内存块。由于资源的这种通用性,它们便能被设置到渲染流水线的不同阶段供其使用。一个常见的例子先把纹理用作渲染目标(即Direct3D绘制到纹理技术),随后再将该资源作为一个着色器资源(即此纹理会经采样用作为着色器输入数据)。不管是充当渲染目标·深度/模板缓冲区还是着色器等角色,仅靠资源本是无法体现出来的。而且,我们有时只希望将资源中的部分数据绑定之渲染流水线,但如何从整个资源中将它们提取出来呢?在这创建一个数据可能用的是无类型格式,这样的话,GPU甚至不知道这个资源的具体格式。
解决上述问题就是引入描述符的原因。除了指定资源数据,描述符还会为GPU解释资源:它们会告知Direct3D某个资源该如何使用(即该资源将被绑定到渲染流水线的哪个阶段上),而且我们可借助描述符来指定欲绑定资源的局部数据。这就是说,如果某个资源在创建的时候采用的无类型格式,那么我们必须在为它创建描述符时指明其具体类型。
注意
视图(view)与描述符(descriptor)是同义词。“视图”虽是Direct3D先前版本中的常用术语,但它仍然沿用在Direct3D 12的部分API中。例如,“常量缓冲区视图(constant buffer view)”与“常量缓冲区视图(constant buffer description)”表达的是同一事物。
每个描述符都有一种具体类型,此类型指明了资源的具体作用。常用的描述符如下:
1.CBV/SRV/UAV 描述符分给表示的是常量缓冲区视图(costant buffer view)、着色器资源视图(shader resource view)和无序访问视图(unordered buffer view)这三种资源。
2.采样器(sampler,亦有译为取样器)描述符表示的是采样器资源(用于纹理贴图)。
3.RTV描述符表示的是渲染目标视图资源(render target view)。
4.DSV描述符表示的是深度/模板视图资源(depth/stencil view)。
描述符堆(descriptor heap)中存在一系列描述符(可将其看成是描述符数组),本质上是存放用户程序中某种特定描述符的一块内存。我们需要为每一种类型的描述符创建出单独的描述符堆。另外,也可以为同一种描述符类型创建多个描述符堆。
我们能用多个描述符来引用同一个资源。例如,可以通过多个描述符来引用同一个资源中不同的局部数据。而且,前文曾提到过,一种资源可以绑定到渲染流水线的不同阶段。因此,对于每个阶段都需要独立的描述符。例如,当一个纹理需要被用作渲染目标与着色器资源时,我们就要为它分别创建两个描述符:一个RTV描述符和一个SRV描述符。类似地,如无类型格式创建了一个资源,又希望该纹理中的元素可以根据需求当作浮点值或整数值使用,那么就需要为它分别创建两个描述符:一个指定为浮点格式,另一个指定为帧数格式。
创建描述符的最佳时机为初始化期间。由于在此过程中需要执行一些类型的检测和验证工作,所以最好不要在运行时(runtime)才创建描述符。
注意
2009年8月的SDK文档写到:“所谓创建一个完整类型的资源,即在创建的伊始就确定了它的具体格式。这将使运行时的访问操作得到优化[.. .. ..]。”因此,当确实需要用到无类型资源多带来得灵活性时(即根据不同视图对同一种数据进行多种不同解释的能力),再以这种方式来创建资源,否则应创建完整类型的资源。