核心概念:什么是 HRESULT? (The Why)#

从技术上讲,HRESULT 是一个 32位整数(long),它是 COM (Component Object Model) 组件的标准返回值。DirectX 是基于 COM 构建的,所以沿用了这一标准。

HRESULT 是与图形硬件沟通的第一道防线,它是 API 调用的“心跳报告”。

在 D3D12 中,几乎所有的 API 调用(创建设备、分配内存、编译着色器)都会返回一个 HRESULT。如果你忽略它,你就是在蒙眼狂奔。

HRESULT 是一个变量类型(C++中的 long),它是 Windows 编程中用来记录函数执行结果(包括错误类型、成功状态)的标准载体。

在 DirectX 12 和 Windows COM 编程中,它不仅记录“出错了”,还记录“错哪了”甚至“成功了但有些小状况”。

在 C++ 的头文件定义中,它其实就是一个 32 位的整数: typedef long HRESULT;所以,当你看到 HRESULT hr = … 时,你本质上是在存一个数字。 但是,这个数字并不是随机生成的,它像一个“比特位地图”。

2. 它的内部结构(为什么它是 32 位的?)#

虽然它只是一个整数,但它的 32 个二进制位(bit)被划分成了三个特定的区域。这就好比身份证号码,每一段数字都有特定含义。

我们通常用 16 进制来看它,比如常见的失败代码 0x80070057:

  • 第 31 位 (Severity - 严重性):最重要的一位!
  • 0 = 成功 (Success)
  • 1 = 失败 (Fail)
  • 这就是为什么所有的错误代码(如 0x8...)开头都是 8(二进制 1000...),因为第 31 位是 1。
  • 第 16-30 位 (Facility - 来源):表示是谁报错的。
  • 比如 0x007 代表 Win32 API,0x87A 代表 DXGI (DirectX Graphics Infrastructure)。
  • 第 0-15 位 (Code - 具体代码):具体的错误原因。
  • 比如 5 代表 "Access Denied" (拒绝访问),57 代表 "Invalid Argument" (参数无效)。

throw的含义:

GUID : 它本质上是一个 128位 (16字节) 的超大整数, 是每一个组件的“身份证”

IID_PPV_ARGS :

实现代码:#define IID_PPV_ARGS(ppType) __uuidof(**(ppType)), (void**)(ppType)

1.自动获取GUID,防止自己手误填错;

/*假设你传入的是 &device:

1)device 的类型是 ID3D12Device*(指向设备的指针)。

2)ppType (也就是 &device) 的类型是 ID3D12Device**(指向指针的指针)。

3)*(ppType) 解引用一次,变成了 ID3D12Device*。

4)**(ppType) 再解引用一次,变成了 ID3D12Device (类型本身)。

结论:__uuidof 直接去查“你传入的这个变量所属的类型”的身份证号。 如果你传的是 device,它就自动填 ID3D12Device 的 GUID。你根本没有机会填错*/

2.进行类型转换,返回指向函数的指针

Fence:

实质: Fence 本质上就是一个所有的 CPU 和 GPU 都能访问的 64位整数 (UINT64)

e.g. 1.

2.

ps:这个方法是强制刷新命令队列,cpu会一直对单帧的绘制进行等待;后面用到了其他的方法,叫做双缓冲或三缓冲,意思是在提交了第一帧的命令之后,CPU不会傻等,而是去填写第二帧与第三帧命令,此时只有当第一帧还没有绘制完时,CPU才会等待

获取描述符大小:

作用:

设置MSAA抗锯齿属性:

flag:

D3D12 的硬件兼容性检查非常严格。

  • 你不能直接问“支持 MSAA 吗?”
  • 你必须精确地问:“对于 R8G8B8A8 这种颜色格式,做 4倍 采样,你支不支持?”
  • 如果你换了颜色格式(比如 DXGI_FORMAT_R16G16B16A16_FLOAT HDR格式),你得重新问一次,因为显卡可能支持普通颜色的 MSAA,但不支持 HDR 颜色的 MSAA。

命令队列和命令列表

CPU 使用命令列表(List)作为工具,将指令数据写死在分配器(Allocator)提供的内存上,最后将整块内存的引用交给队列(Queue)去执行

交换链:

交换链本质就是观众在看A黑板而这时候你在写B黑板,之后在把B黑板调换位置,观众永远能看见完整画面

CD3DX12_CLEAR_VALUE optClear:

资源的转换:

关于为什么每次使用comptr类型变量都要调用Get()方法:

窗口的初始化:

在 Windows 中,你不能直接说“给我个窗口”。你必须先填写一张详细的“申请表”,定义这个窗口的行为和长相:WNDCLASS wc;

为什么rtv和dsv要用句柄访问而不是直接使用堆上的数据:

D3D12_RESOURCE_STATE_PRESENT 是 CPU、GPU 渲染核心、显示控制器三者之间的一个契约。

它保证了当屏幕读取数据时,数据是完整的、可读的且不再被修改的。如果你不切换到这个状态直接 Present,Debug Layer 会直接报错,而在真机上,你可能会看到花屏、闪烁或者显卡驱动崩溃。

深度/模板测试:

Part1 :
实现镜面效果——

一种方法是模板测试,但这种方法仅局限于不产生形变的平面镜。

首先沿着镜面的对称轴(镜面Local Space的x轴(应该是吧?))再创建一个一样的模型,接着根据对称轴创建镜像矩阵,将光照方向也镜像,这个光照将用于镜像物体的渲染

之后我们需要在RenderItem结构体中添加layer成员变量,用于控制渲染过程使用的不同的passconstant : 在BuildRenderItems()中赋予属性,在Draw()中进行调用

原文:DX12