DirectX基础【0】

——介绍和开发预备知识

Posted by HciDsi on December 16, 2023

前言

在此之前一直使用Unity引擎来进行游戏开发与计算机图形学的学习,但是随着深入地学习与实践,越来越感觉直接使用成熟的引擎学习的局限性。

所以开始学习DirectX12,跟着《龙书》学习,记录一下自己学习中的理解和想法。刚刚开始学习,希望大佬们可以指正,可以和同学们交流。

DirectX12介绍

DirectX 12(DX12)是微软开发的一种图形API,用于高性能游戏和图形应用程序的开发。相较于之前的DirectX版本,提供了更底层的硬件访问和更多的控制权。有着以下的优势:

  1. 更底层的硬件访问:

    DX12提供了更接近硬件的访问,使得开发者可以更直接地控制GPU。这使得程序员可以更好地优化性能,减少CPU开销,充分发挥现代GPU的强大性能。

  2. 多线程渲染:

    DX12引入了显式的多线程渲染支持,允许应用程序更灵活地管理和控制渲染线程。这可以提高多核CPU的利用率,减少CPU瓶颈。

  3. 更高的图形API效率:

    DX12通过减少API调用的次数、优化命令流水线,以及提供更灵活的资源管理,降低了图形API的开销,从而提高了应用程序的性能。

  4. Direct3D 11和Direct3D 9向后兼容性:

    DX12支持Direct3D 11和Direct3D 9应用程序的向后兼容性,使得现有的游戏和应用程序能够在新的DX12平台上运行,而无需全面重写。

而目前市场上DirextX游戏占据绝对的主导地位。

DirectX12开发预备知识

组件对象模型

DirectX使用的组件对象模型(Component Object Model,COM)是一种微软提出的二进制接口标准,用于实现组件化的软件设计。通常将COM视为一种接口。我们只需要知道:

  • 获取COM接口指针不是new一个接口,而是使用特殊的函数或另一个COM接口方法。
  • COM会自己统计引用次数,当接口使用完后调用Release删除接口对象,当COM调用为0时,COM将自行释放内存。

我们可以使用Microsoft::WRL::ComPtr类作为COM的智能指针,可以使我们更加便捷的对Com接口进行管理。ComPtr也提供了一些好用的方法,比如Get() 返回指向此底层Com接口的指针,GetAddressOf()返回指向此底层Com接口的地址,Reset()将对象设置为nullptr并释放所有与之相关引用(同时会减少COM接口引用计数)。

交换链

交换链(Swap Chain)是用于在前台和后台缓冲区之间进行切换的一种技术。交换链的主要目的是为了提供流畅的图形渲染,避免因为绘制速度和显示速度不一致而导致的画面撕裂和卡顿现象。

在渲染时:

  • 前台缓冲区: 用户实际看到的内容,由交换链直接显示在屏幕上。
  • 后台缓冲区: 正在绘制的下一帧,绘制完成后由交换链将两种缓冲区角色互换,后台缓冲区变为前台缓冲区,绘制在屏幕上。

Swapchin

  • 双缓冲: 使用两个缓冲区(前台和后台),渲染时将结果呈现到后台缓冲区,再将后台缓冲区的内容交换到前台。

  • 多缓冲: 使用多个缓冲区,通常是三个以上,可以更好地平衡渲染和显示的速度。

最常见的是使用两个缓冲区的情况,有时候也会使用更多缓冲区的情况。

在DirectX中以ID3DGISwapChain接口表示交换链,ID3DGISwapChain不仅可以管理前台缓冲区和后台缓冲区,也提供了一些对缓冲区操作的方法,之后会陆续使用到。

资源与描述符

资源是GPU中存储图形数据的对象,可以包括缓冲区(Buffer)、纹理(Texture)、常量缓冲区(Constant Buffer)等。这些资源存储了图形渲染所需的数据,如顶点数据、纹理数据、着色器常量等。

在GPU渲染时,需要对资源进行读取和写入,但是GPU资源只是一些普通的内存块,GPU需要何时对资源进行读取和操作?如何在所有的GPU资源中取到正好需要的资源?若使用到无类型资源,GPU甚至无法得知资源的具体类型?为了解决这个问题我们需要为资源添加描述符。

描述符用于描述资源或其他GPU资源的属性和位置。描述符通常用于在GPU上设置各种资源的引用,例如设置纹理、缓冲区、着色器资源等。指定了资源描述符,GPU将可以得到实际的资源数据,也可以了解资源的信息。资源描述符将告知GPU资源将如何使用(既资源将绑定在渲染流水线的哪个阶段)。

常用的几种描述符:

  1. 渲染目标视图描述符(RTV):用于描述渲染目标视图的属性,例如关联的资源类型、资源格式等。
  2. 深度模板视图描述符(DSV):用于描述深度模板视图的属性,包括关联的资源类型、资源格式、使用的深度模板格式等。
  3. 着色器资源视图描述符(SRV):用于描述着色器资源视图的属性,例如关联的资源类型、资源格式、资源在着色器中的访问方式等。
  4. 常量缓冲区视图描述符(CBV):用于描述常量缓冲区视图的属性,包括关联的常量缓冲区的大小。
  5. 无序访问视图描述符(UAV):用于描述无序访问视图的属性,例如关联的资源类型、资源格式、在着色器中的访问方式等。
  6. 采样器描述符(Sampler):于描述采样器的属性,例如过滤方式、边界模式、向导等。

在DirectX中描述符主要通过描述符堆(Descriptor Heap)来管理。可以将其看作描述符数组,有时我们需要对一个GPU资源添加多个描述符,比如一个资源在渲染流水线的多处使用,我们需要为每个阶段设置不同的描述符,比如我们希望一个无类型的资源可以根据需要作为浮点数或整数,我们需要为他创建浮点数和整数格式描述符。

最好在初始化阶段创建描述符,由于创建描述符时需要进行一些类型检测和验证工作,因此避免在运行过程中才创建描述符。

命令队列和命令列表

命令队列(Command Queue)和命令列表(Command List)是用于与GPU通信和执行渲染操作重要媒介。命令队列本质是一个环形缓冲区,CPU可以使用命令列表将命令提交到命令队列中,提交的命令不会立即执行,GPU先处理命令队列最下层的命令,新的命令会等待到处于命令队列最下层而后执行。

CommandQueue_and_CommandList

在DirectX中命令队列和命令列表分别被抽象成ID3D12CommandQueueID3D12CommandList,我们可以使用继承于ID3D12CommandList接口的ID3D12GraphicsCommandList接口中提供的一系列方法向命令列表中添加命令。最后使用ID3D12CommandQueue::ExecuteCommandLists 方法向命令队列提交命令。需要注意提交命令列表前必须使用ID3D12CommandList::Close关闭命令列表。

我们可以使用命令分配器(Command Allocator)来分配和管理命令列表。命令分配器用于为命令列表分配内存和管理其状态。

结语

这算是DirectX学习的开始,介绍了一些DirectX的相关信息,和进行DirectX开发需要的一些预备知识。