2.1.1资源概述-创建资源
在许多情况下,对现代实时渲染技术的讨论往往围绕着可编程管道以及如何使用它。管道确实是实时渲染难题中至关重要的一部分,尤其是因为图形开发人员的主要任务之一是编写用于管道的着色器程序。然而,在这些讨论中,连接到管道的内存资源的重要性往往被忽视。同样重要的是,要了解特定算法需要什么资源,这些资源将在哪里使用,以及如何使用。
本章将深入探讨Direct3D11内存资源的主题。我们首先确定Direct3D 11 API如何组织和管理资源,然后考虑两种主要类型的资源——缓冲区和纹理。我们将详细讨论资源的每个子类型、如何创建、如何使用以及在不再需要时如何释放。关于资源使用的讨论还将介绍资源视图的概念,这是一种用于将资源连接到管道中特定位置的适配器。
任何关于资源的讨论都必须同时考虑资源使用的两个方面。该应用程序涉及创建、填充资源以及将资源连接到管道。然而,我们也必须考虑如何在管道内部使用资源。如何在可编程着色器中声明和使用它们,与正确创建资源一样重要。详细讨论本主题是为了更好地理解着色器程序中的这种用法,将在第3-7章中进行构建。
正如我们将在本章后面看到的,在创建资源之前,清楚地了解资源将如何使用是非常重要的,因为资源的使用域是由其创建输入参数预先确定的。如果配置不正确,资源可能会对应用程序产生一些负面影响,从性能一直很差到由于错误而完全无法执行管道。这使得资源确实成为一个非常重要的话题!
2.1资源概述
Direct3D11中有两类基本资源:缓冲区和纹理。其中每个都有几个子类型,每个子类型都有不同的配置选项。用于创建资源的可用选项的数量一开始可能会有点太多,因此我们将从如何在Direct3D 11 API中组织这些选项开始讨论该主题。通过从高水平开始,我们可以开始了解各种资源中的差异,然后在这些知识的基础上,进一步了解这些差异决定如何使用资源的细节。
Direct3D11资源类结构的组织如图2.1所示。在这里,您可以看到只有四个资源类可用于整个API-Buffers,以及ID、2D和3D纹理。
该图还显示,这些资源类中的每一个都派生自一个名为ID3D11Resource的公共基类。这是有道理的,并表明了我们将在本章中重新审视的一个共同主题。也就是说,资源只是可以附加到管道并用于输入或输出(或者在某些情况下,同时用于输入和输出)的内存块。换句话说,资源是可供GPU使用和操作的内存块。尽管它们有不同的名称并支持不同的概念,但它们之间唯一真正的区别是它们与管道绑定的语义以及关于它们的格式、访问和使用的规则。
2.1.1创建资源
如第1章“Direct3D11概述”所述,ID3D11Device接口负责创建所有内存资源。然后,创建的资源可以直接或通过资源视图附加到管道,然后在管道执行事件期间使用这些资源。资源创建过程对每种类型的资源使用不同的ID3DllDevice方法,但它们都遵循相同的通用模式。
HRESULT CreateBuffer(
[in] const D3D11_BUFFER_DESC *pDesc,
[in, optional] const D3D11_SUBRESOURCE_DATA *pInitialData,
[out, optional] ID3D11Buffer **ppBuffer
);
HRESULT CreateTexture1D(
[in] const D3D11_TEXTURE1D_DESC *pDesc,
[in, optional] const D3D11_SUBRESOURCE_DATA *pInitialData,
[out, optional] ID3D11Texture1D **ppTexture1D
);
HRESULT CreateTexture2D(
[in] const D3D11_TEXTURE2D_DESC *pDesc,
[in, optional] const D3D11_SUBRESOURCE_DATA *pInitialData,
[out, optional] ID3D11Texture2D **ppTexture2D
);
HRESULT CreateTexture3D(
[in] const D3D11_TEXTURE3D_DESC *pDesc,
[in, optional] const D3D11_SUBRESOURCE_DATA *pInitialData,
[out, optional] ID3D11Texture3D **ppTexture3D
);
创建方法都采用三个参数。第一个参数是一个结构,它指定了可以创建资源的所有各种选项。它被称为资源描述。每种资源类型都使用自己的描述结构,因为它们都有一组不同的可用属性,但这些结构都有相同的目的——定义所创建资源的所需特征。本章将详细研究这些结构,它们的正确配置包括使资源按您的意愿运行所需的大量工作。资源创建方法中的第二个参数是指向D3D11_SUBRESOURCE_DATA结构的指针,该结构用于提供要加载到资源中的初始数据。例如,如果缓冲区资源将保存静态顶点数据,则此结构将用于将模型的顶点数据传递到缓冲区中。这消除了在创建缓冲区后手动操作缓冲区的需要,如果缓冲区的内容不会改变的话。如果不需要初始化,也可以将此参数设置为null。最后一个参数是指向适当资源接口的指针的指针,在成功的资源创建事件之后,创建的资源指针存储在该接口中。
在这些方法中的每一种方法中,实际配置都出现在资源描述结构中。如上所述,每个资源类型都有自己的结构,用于定义其属性。然而,有一些共同的元素是在所有结构中共享的。其中包括使用标志、绑定标志、CPU访问标志和其他标志。由于这些参数在所有各种资源类型中都是通用的,我们将在这里详细讨论它们。某些类型的资源所特有的各个参数将在本章稍后的资源类型部分中进行讨论。
资源使用标识
我们将检查的第一个资源参数是使用规范。简单地说,这个参数用于指定应用程序打算对资源做什么。一般来说,我们可以将资源视为计算机中某个位置的内存块。视频存储器和系统存储器都可以保存资源,并且可以通过Direct3D11运行时将资源复制到每种类型的存储器以及从每种存储器复制资源。要优化资源所在的位置以及运行时/驱动程序内部处理资源的方式,应用程序必须使用此用法参数指定其意图。这与以前版本的Direct3D不同,前者允许用户指定资源所在的内存池。但是,通过指示资源的使用意图,可以提供类似级别的控制。可用值的列表封装在D3D11_USAGE枚举中,如清单2.1所示
enum D3D11_USAGE {
D3D11_USAGE_DEFAULT,
D3D11_USAGE_IMMUTABLE,
D3D11_USA6E_DYNAMIC,
D3D11JJSAGE_STAGING
}
这些标志中的每一个指示一种使用模式,该使用模式标识CPU和GPU所需的资源读/写能力。例如,仅由GPU读取和写入且从不由CPU访问的资源可以安全地放置在视频存储器中。它永远不会传输回系统内存,因为CPU无法访问它。这通过使资源更靠近GPU来提供优化。表2.1显示了每个标志的读/写权限。
Resource Usage | Default | Dynamic | Immutable | Staging |
---|---|---|---|---|
GPU-Read | yes | yes | yes | yes |
GPU-Write | yes | yes | ||
CPU-Read | yes | |||
CPU-Write | yes | yes |
- Immutable usage不可变的用法。最简单的用法模式是不可变用法。使用此使用模式创建的资源只能由GPU读取,CPU无法访问。由于GPU或CPU无法写入资源,因此创建后无法对其进行修改。这要求在创建时使用其数据对资源进行初始化。由于资源无法修改,因此使用类型的名称是不可变的。这种资源类型的常见示例是静态常量、顶点和索引缓冲区,这些缓冲区是用在应用程序生命周期内不会更改的数据创建的。通常,这些资源用于向GPU提供数据。
- Default usage 默认用法。从上表中可以看出,默认使用提供对GPU的读写访问,但不提供对CPU的访问。这是任何资源的最佳使用设置,这些资源不仅将由GPU使用,而且还将在某个时刻由GPU修改。这种使用模式的一些常见示例是渲染纹理(渲染到GPU中并随后从中读取)和流输出顶点缓冲区(其具有由GPU流式传输到其中的数据并随后由GPU读取用于渲染)。由于这种类型的资源可以保留在视频存储器中,GPU将以最快的速度访问它们,应用程序的整体性能也将受益。
- Dynamic usag 动态使用。动态使用是允许CPU写入然后可以由GPU读取的资源的第一种使用类型。在这种使用情况下,资源的内容由CPU提供,然后由GPU消耗。最常见的例子是常量缓冲区,它为每个渲染帧中都会发生变化的可编程着色器阶段提供渲染数据。例如,这种类型的数据可以是变换矩阵。一个重要的考虑因素是CPU无法读回资源的内容——数据只能在一个方向上从CPU流向GPU。
- Staging usage暂存使用情况。分段使用是最终使用标志,它提供了一种特殊类型的使用模式。到目前为止,我们讨论的使用模式已经涵盖了执行渲染时的典型资源使用场景。然而,在许多情况下,我们希望使用GPU处理数据,然后将其读回CPU进行存储或检查。随着具有DirectCompute技术的GPGPU设施的引入,以及通过流输出级流式传输顶点信息的能力,可以使用GPU处理信息,然后将其读回CPU。与其强制其他使用模式允许对CPU进行读取访问,不如将具有分段使用的资源用作中间资源。
基本概念是,GPU可以操纵所需的资源,然后将其复制到暂存资源上,然后再读回CPU。这需要创建一个额外的暂存资源来检索数据,但它允许GPU使用的其他资源尽可能靠近GPU,而不关心CPU访问。我们将在本章后面看到有关操作资源内容的更多详细信息。
CPU访问标识
在确定了资源的使用标志之后,必须选择所需的CPU访问标志。CPU访问标志公开了与我们在use属性中看到的类似的信息,但它仅限于定义CPU对资源的可能访问。只有两个可能的标志可用于CPU访问标志。
enum D3D11_CPU_ACCESS_FLA6 {
D3D11_CPU_ACCESS_WRITE,
D3D11_CPU_ACCESS_READ
}
这些标志可以与按位“或”运算相结合,以指示CPU是读取、写入还是读取和写入资源。这些设置还必须考虑为此资源指定的使用标志。如上表所示,CPU可以读取和写入暂存资源,从而允许设置这两个标志。相反,CPU根本无法访问默认使用情况,因此其CPU访问标志必须设置为0。
绑定标识
我们的下一个公共资源标志是绑定标志。此属性指示资源可以在管道上的何处绑定,并包括表示每个可用绑定位置的各种标志。这些标志也可以与逐位OR组合,以允许为同一资源定义多个绑定位置。如果创建的资源没有相应的标志,并且应用程序试图将资源绑定到管道,则会导致错误。所有可用的绑定位置如清单2.3中D3D11_BIND_FLAG枚举中所示。
enum D3D11_BIND_FLAG {
D3D11_BIND_VERTEX_BUFFER,
D3D11_BIND_INDEX_BUFFERJ
D3D11_BIND_C0NSTANT_BUFFER,
D3D11_BIND_SHADER_RESOURCE,
D3D11_BIND_STREAM_OUTPUT,
D3D11_BIND_RENDER_TARGET,
D3D11_BIND_DEPTH_STENCIL,
D3D11_BIND_UNORDERED_ACCESS
}
从这个列表中可以看到,只有八种类型的位置可以将资源绑定到管道。顶点和索引缓冲区标志用于声明将附加到输入装配程序阶段以向管道提供几何体的资源,而渲染目标和深度模板标志允许资源连接到输出合并阶段以从管道接收渲染输出。流输出标志也允许从管道输出,尽管它接收几何图形而不是光栅化的图像数据。这些标志都表示管道上的单个绑定点。
比如将纹理作为渲染目标(即,Direct3D 渲染到纹理)或着色器资源(即,在着色器中对纹理进行采样),创建用于这两种目的的纹理资源时,应使用绑定标志值:D3D11_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE
相反,D3D11_BIND_C0NSTAND_BUFFER、D3D11_BIND_SHADER_RES0URCE、D3D11_BIND_UN0RDERED_ACCESS访问标志都指示资源可以绑定到一些或所有可编程着色器级以在着色器程序中使用。这些用法将在本章稍后更详细地讨论,但通常必须确保为所需的管道连接点设置正确的绑定标志。这些绑定点的概述以及它们在管道上的位置如图2.2所示。
杂项标识Miscellaneous Flags
我们将看到的最后一个共同特性是杂项标志。这组标志封装了资源可以使用的大多数特殊情况属性。其中一些标志允许在非传统情况下使用资源,例如与GDI绘图交互操作或在多个ID3D11设备实例之间共享资源。可用的标志如清单2.4所示。
enum D3D11_RES0URCE_MISC_FLAG {
D3D11_RESOURCE_MISC_GENERATE_MIPS,
D3D11_RESOURCE_MISC_SHARED,
D3D11_RESOURCE_MISC_TEXTURECUBE,
D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS,
D3D11_RESOURCE_MISC_BUFFER_ALO0W_RAW_VIEWS,
D3D11_RESOURCE_MISC_BUFFER_STRUCTURED,
D3D11_RESOURCE_MISC_RES0URCE_CLAMP,
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX,
D3D11_RESOURCE_MISC_GDI_C0MPATIBLE
}
由于标志的复杂性,在介绍适当内容后,将在本章剩余部分更详细地讨论其中一些标志。然而,我们将在这里简要讨论其中一些旗帜,因为它们更具普遍性。D3D11_RESOURCE_MISC_SHARED标志指示资源可以在多个ID3DllDevice实例之间共享。这只用于高级应用程序,在本书的剩余部分中不会进一步讨论。类似地,D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX标志指示一个资源将由多个设备使用,但它也支持用于共享资源控制的互斥系统。这一功能也没有在本书的剩余部分中讨论。最后,我们有D3D11_RESOURCE_MISC_GDI_COMPATIBLE标志,它表示资源可以与GDI互换使用。该功能在一些演示程序中用于实现文本渲染,但本书中没有进一步描述。
资源释放
在本章中,我们将探讨开发人员可以使用的各种类型的内存资源。所有这些资源类型都实现COM接口,并且它们最终在继承链中拥有IUnkown接口。这意味着它们被引用计数,并且必须在应用程序使用完它们之后才能发布。应用程序必须小心跟踪其保留的资源引用并正确释放它们,否则将导致内存泄漏。