DirectX 12 学习之路(二)

一、COM
COM 组件对象模型(Component Object Model) 微软的一套软件组件的二进制接口标准,使得跨编程语言的进程间通信、动态对象创建成为可能。
https://zh.wikipedia.org/wiki/%E7%BB%84%E4%BB%B6%E5%AF%B9%E8%B1%A1%E6%A8%A1%E5%9E%8B

二、严格意义上DX12并不直接与本地窗口系统交互,DX12先渲染到DXGI交换链缓冲,然后DXGI再将交换链缓冲中的数据呈现到窗口显示。从Direct3D10开始,由DXGI负责与本地窗口系统交互,Direct3D在DXGI的基础上构建。

三、SIMD(Single Instruction Multiple Data)单指令多数据寄存器(宽SIMD可有128位),利用一条SIMD指令即可同时对4个32位浮点数或整数进行运算。SSE2(Streaming SIMD Extension 2)SIMD流指令扩展2,还有一个SSE。

四、用DirectXMath库的XMVECTOR类型来描述向量,这样就可以在代码中利用SIMD技术进行高效运算。对于类中的数据成员,要使用XMFLOAT2、XMFLOAT3和XMFLOAT4这些类表示向量,并通过加载和存储方法令数据在XMVECTOR类型与XMFLOATn类型之间相互转化。另外,在使用常向量的初始化语法时,应采用XMVECTORF32类型。

五、为了提高效率,当XMVECTOR类型的值被当作参数传入函数时,可以直接存入SSE/SSE2寄存器中而不是栈上。要令代码和平台无关,我们将使用FXMVECTOR、GXMVECTOR、HXMVECTOR和CXMVECTOR类型来传递XMVECTOR参数。传递XMVECTOR参数的规则为:前3个XMVECTOR参数应当用FXMVECTOR,第4个XMVECTOR参数用GXMVECTOR,第5个和第6个XMVECTOR参数使用HXMVECTOR类型,而其余的XMVECTOR类型参数使用CXMVECTOR类型。

六、XMVECTOR类重载了一些运算符来实现向量的加法、减法和标量乘法。另外,DirectXMath库还提供了一些实用函数,用于计算向量的模、模平方、两个向量的点积、两个向量的叉积及对向量进行规范化处理:
XMVECTOR XM_CALLCONV XMVector3Length(FXMVECTOR V);
XMVECTOR XM_CALLCONV XMVector3LengthSq(FXMVECTOR V);
XMVECTOR XM_CALLCONV XMVector3Dot(FXMVECTOR V1,FXMVECTOR V2);
XMVECTOR XM_CALLCONV XMVector3Cross(FXMVECTOR V1,FXMVECTOR V2);
XMVECTOR XM_CALLCONV XMVector3Normalize(FXMVECTOR V);

七、在比较浮点数时,一定要注意浮点数存在误差。我们认为相等的两个浮点数可能会因此而有细微的差别。例如,已知在数学上规范化向量的长度为1,但是在计算机程序中的表达上,向量的长度只能接近于1。此外,在数学中,对于任意实数p有1的p幂次方等于1,但是,只能在数值上逼近1时,随着幂p的增加,所求近似值的误差也在逐渐增大。数值误差是可以积累的。

八、一个应用程序不一定要用到模板缓冲区,但一经使用,则深度缓冲区将总是与模板缓冲区如影随行,共同进退。例如,32为格式 DXGI_FORMAT_D24_UNORM_S8_UINT ,使用24位作为深度缓冲区,其他8位作为模板缓冲区。DXGI_FORMAT_D32_FLOAT_S8X24_UINT,该格式共64位,其中32位是浮点型深度缓冲区,8位(无符号整数)分配给模板缓冲区,剩余的24位仅用于填充对齐,不作他用。

九、在渲染处理过程中,GPU可能会对资源进行读(例如,从描述物体表面样貌的纹理或者存有3D场景中几何体位置信息的缓冲区中读取数据)和写(例如,向后台缓冲区或深度/模板缓冲区写入数据)两种操作。在发出绘制命令之前,我们需要将与本次绘制调用(draw call)相关的资源绑定(bind 或称 链接,link)到渲染流水线上。部分资源可能在每次绘制调用时都会有所变化,所以我们也就要每次按需更新绑定。但是GPU资源并非直接与渲染管线绑定,而是通过一种名为描述符(descriptor)的对象对它间接引用,我们可以把描述符视为一种对送往GPU的资源进行描述的轻量级结构。从本质上讲,它实际上是一种中间层;若指定了资源描述符,GPU将既获得实际的资源数据,也能了解到资源的必要信息。因此,我们需要把绘制调用需要引用的资源,通过指定描述符的方式绑定到渲染流水线。

十、视图(view)和描述符(descriptor)是同义词。例如,常量缓冲区视图(constant buffer view)和常量缓冲区描述符(constant buffer descriptor)表达的是同一事物。“视图”虽是Direct3D先前版本里的常用术语,但它仍沿用在Direct3D 12的部分API中。每个描述符都有一种具体类型,此类型指明了资源的具体作用。常用的描述符如下:
1.CBV/SRV/UAV描述符分别表示的是常量缓冲区视图(Constant Buffer View)、着色器资源视图(Shader Resource View)、无序访问视图(Unordered access view)这三种资源。
2.采样器(Sampler)描述符,表示的是采样器资源。
3.RTV描述符表示的是渲染目标视图资源(Render Target View)。
4.DSV描述符表示的是深度/模板视图资源(Depth/Stencil View)。

十一、超级采样反走样技术(Supersampling Anti-Aliasing, SSAA),使用4倍屏幕分辨率大小的后台缓冲区和深度缓冲区。3D场景将以这种更大的分辨率渲染到后台缓冲区中。当数据从后台缓冲区调往屏幕显示的时候,会将后台缓冲区按4个像素一组进行解析(resolve,或称为降采样,downsample。把放大的采样点降低回原采样点数):每组用求平均值得方法得到一种相对平滑的像素颜色。因此,超级采样是通过软件的方式提升了画面的分辨率,但是开销昂贵,因为它的每个像素的处理数量和占用的内存大小都增大到了之前的4倍。

十二、多重采样反走样技术(Multisampling Anti-Aliasing,MSAA),通过跨子像素共享一些计算信息,从而使它比超级采样的开销更低。假设采样4X多重采样(即每个像素中都有4个子像素),并同样使用4倍于屏幕分辨率的后台缓冲区和深度缓冲区。值得注意的是,这种技术并不需要对每一个子像素都进行计算,而是仅计算一次像素中心处的颜色,再基于可视性(每个子像素经过深度/模板测试的结果)和覆盖性(子像素的中心在多边形里面还是外面)将得到的颜色信息分享给其子像素。

十三、对于超级采样来说,图像的颜色要根据每一个子像素来计算,因此每个子像素都可能各具有不同的颜色,而以多重采样的方式来求取图像颜色时,每个像素只需计算一次,最后,将得到的颜色数据复制到多边形覆盖的所有可见子像素中。

十四、DirectX图形基础结构(DirectX Graphics Infrastructure, DXGI,也有译为DIrectX图形基础设施)是一种与Direct3D配合使用的API。设计DXGI的基本理念是使用多种图形API中所共有的底层任务能借助一组通用API来进行处理。DXGI的一些常用功能有:交换链和页翻转功能、切换全屏模式、窗口模式、枚举显示适配器、枚举显示设备、枚举所支持的显示模式(分辨率、刷新率等)等这类图形系统信息、。

十五、扫描式显示设备的工作方式有两种:逐行扫描与隔行扫描。将显示设备的屏幕划分为多个行,称之为“场(field)”,奇数行称为奇数场(upper field),偶数行称为偶数场(lower field)。顾名思义,逐行扫描即在显示每一帧画面时都从上至下逐个场连续扫描,但根据人眼的视觉暂留效应,便可每帧仅扫描一种场,交替扫描。

十六、在Direct3D 12中,应用程序通过控制资源在显存中的去留,主动管理资源的驻留情况(即residency。无论资源是否已位于显存中,都可对其进行管理,在Direct3D 11中则由系统自动管理)。因为显存的空间有限,很可能不足以容下整个游戏的所有资源,或用户还有应用程序也在同时使用显存。这里给出一条与性能相关的提示:程序应当避免在短时间内于显存中交换进出相同的资源,这会引起过高的开销。最理想的情况是,所清除的资源在短时间内不会再次使用。

十七、在调用ID3D12CommandQueue::ExecuteCommandLists方法提交命令列表之前,一定要把命令列表关闭。
// 结束记录命令
mCommandList->Close();

十八、在调用ID3D12CommandQueue::ExecuteCommandList©方法之后,我们就可以通过ID3D12GraphicsCommandList::Reset方法,安全地复用命令列表C占用的相关底层内存来记录新的命令集。Reset方法中的参数对应于ID3D12Device::CreateCommandList方法创建命令列表所用的参数。此方法把命令列表恢复为刚创建时的初始状态,我们可以借此继续复用其底层内存,也可以避免释放旧列表再创建新列表这一系列的繁琐操作。注意,重置命令列表不会影响命令列表中的命令,因为相关的命令列表分配器仍在维护着其内存中被命令队列引用的系列命令。

十九、向GPU提交了一整帧的渲染命令后,我们可能还要为绘制下一帧而复用命令分配器中的内存,ID3D12CommandAllocator::Reset方法应用而生。注意,由于命令队列可能引用命令分配器中的数据,所以在没有确定GPU执行完命令分配器中的所有命令之前,千万不要重置命令分配器。

二十、CPU和GPU间的同步。例子:CPU在t1时间点向命令队列添加命令存储物体P的位置信息,CPU在t2时间点向命令队列添加命令绘制物体P,因为向命令队列添加命令不会阻塞CPU,所以CPU会继续执行后续指令,CPU在t3时间点向命令队列添加命令更新物体P的位置信息。如果GPU在绘制物体P之前,CPU就率先覆写物体P的位置信息(t3),则这个行为会产生错误。为了解决这个问题:强制CPU等待,直到GPU完成所有命令的处理,达到某个指定的围栏点(fence point)为止。我们将这种方法称为刷新命令列表(flushing the command queue),可以通过围栏(fence)来实现这一点。
在重置命令分配器之前,先通过刷新命令列表来确定GPU的命令已执行完毕。

二十一、资源转换。当GPU得写操作还没完成或甚至还未开始,却开始读取资源,便会导致资源冒险(resource hazard)。为此,Direct3D专门针对资源设计了一组相关状态。资源在创建开始会处于默认状态,该状态将一直持续到应用程序通过Direct3D将其转换(transition)为另外一种状态为止。这就使GPU能够针对资源状态转换与防止资源冒险作出适当额行为。通过命令列表设置转换资源屏障(transition resource barrier)数组,即可指定资源的转换。

二十二、命令与多线程。注意问题:
1.每个线程通常都只使用各自的命令列表。
2.每个线程一般都仅使用属于自己的命令分配器。
3.多线程可以访问同一命令队列,也能够同时调用它的方法。特别是每个线程都能同时向命令队列提交它们自己所生成的命令列表。
4.出于性能的原因,应用程序必须在初始化期间,指出用于并行记录命令的命令列表最大数量。

二十三、IID_PPV_ARGS(ppType) 辅助函数的本质是将ppType强制转换为void**类型,获取ppType的COM接口ID(globally unique identifier,全局唯一标识符,GUID)。

二十四、为了辅助用户管理COM对象的生命周期,Windows运行时库(Windows Runtime Library,WRL)专门为此提供了Microsoft::WRL::ComPtr类(#include <wrl.h>),我们可以把它当作是COM对象的智能指针。当一个ComPtr实例超出作用域范围时,它会自动调用相应的COM对象的Release方法,省掉了我们手动调用的麻烦。

二十五、初始化Direct3D
1、用D3D12CreateDevice函数创建ID3D12Device接口实例。
2、创建一个ID3D12Fence对象,并查询描述符的大小。
3、检测用户设备对4X MSAA质量级别的支持情况。
4、依次创建命令队列、命令列表分配器和主命令列表。
5、描述并创建交换链。
6、创建应用程序所需的描述符堆。
7、调整后台缓冲区的大小,并为它创建渲染目标视图。
8、创建深度/模板缓冲区及与之关联的深度/模板视图。
9、设置视口(viewport)。
10、设置裁剪区域(scissor rectangle)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章