WRITING · 文章

第 1 讲:理解 DirectX 12 的运行模型

ZRonin

1. DirectX 12 到底是什么?

首先,DirectX 和 Direct3D 不是完全相同的概念。

DirectX 是微软的一组多媒体与图形相关技术,而 Direct3D 12(D3D12) 是其中负责 3D 图形编程的 API。我们平时所说的 DX12 图形开发,主要就是指 D3D12。

如果已经接触过 OpenGL,例如:

glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);

在 OpenGL 中,我们通过这些函数提交渲染操作,驱动和运行时负责处理不少内部细节。

DX12 则要求程序员更明确地管理 GPU 工作的组织方式。

看一下两者的概念对比:

OpenGLDirectX 12OpenGL ContextD3D12 Device(并非直接等价)隐式状态管理较多显式状态管理更多glDrawArrays()DrawInstanced()GLSLHLSL缓冲区和纹理对象Resource驱动处理较多同步细节开发者负责更多同步工作

这种设计给了渲染器开发者更精细的控制权,但也意味着你必须自己处理更多资源生命周期和同步问题。

2. DX12 是如何控制 GPU 的?

先认识最重要的三个对象。

                                                        CPU / C++ 程序

                                      组织资源、创建对象、记录绘制命令

                                                                 ↓

                                           Command List(命令列表)

                                              记录 GPU 应执行的操作

                                                                 ↓

                                           Command Queue(命令队列)

                                                   接收并提交 GPU 工作

                                                                 ↓

                                                           GPU 执行

                                        执行绘制或计算任务,产生渲染结果

这是简化的命令提交流程。实际上,DX12 还涉及内存、资源状态和 CPU/GPU 同步。

其中:

  • Device:代表用于创建和管理 D3D12 资源及相关对象的逻辑设备接口。
  • Command List:可以理解成记录 GPU 操作的命令清单。
  • Command Queue:负责将这些命令提交给 GPU 执行。

注意,CPU 提交命令并不等于 GPU 已经执行完成。这正是后面学习 Fence(围栏同步)的原因。

3. 第一次 DX12 编程

本节不急着绘制三角形,先完成一个最小实验:

使用 C++ 成功创建一个 D3D12 Device。


#include <iostream>
#include <d3d12.h>
#include <wrl/client.h>

#pragma comment(lib, "d3d12.lib")

using namespace std;
using Microsoft::WRL::ComPtr;

int main()
{
    ComPtr<ID3D12Device> device;

    HRESULT hr = D3D12CreateDevice(
        nullptr,
        D3D_FEATURE_LEVEL_11_0,
        IID_PPV_ARGS(device.GetAddressOf())
    );

    if (FAILED(hr))
    {
        cout << "DX12 Device 创建失败!" << endl;
        return 1;
    }

    cout << "DX12 Device 创建成功!" << endl;

    return 0;
}

如果成功,控制台会输出:

                                                     DX12 Device 创建成功!

这表示你成功初始化了一个 DX12 设备接口,但尚未创建窗口或绘制图像。

4. 关键代码讲解

第一句:

ComPtr<ID3D12Device> device;

ID3D12Device 是 DX12 的设备接口。

ComPtr 是一种管理 COM 接口指针的智能指针,可以在合适的时候自动释放接口引用,降低资源泄漏的风险。

你可以暂时将它理解为:用一个对象来安全管理指向 DX12 接口的指针。

第二句:

D3D12CreateDevice(
    nullptr,
    D3D_FEATURE_LEVEL_11_0,
    IID_PPV_ARGS(device.GetAddressOf())
);

这是创建设备的核心函数,它的三个实参分别表示:

参数含义nullptr使用默认显示适配器D3D_FEATURE_LEVEL_11_0要求至少支持该硬件功能级别IID_PPV_ARGS(...)指定接口类型并传入接收设备接口指针的位置

这里的 11_0 是 Feature Level,不代表我们正在使用 DirectX 11 API。整个程序依然使用 D3D12。

第三句:

HRESULT hr = D3D12CreateDevice(...);

HRESULT 用来表示调用结果。

if (FAILED(hr))

则检查这次操作是否失败。

这也是以后 DX12 编程中会反复出现的错误处理模式。

评论