一. 编程模型:组件对象模型 (COM)
DirectX 11 的 API 是基于 COM (Component Object Model) 标准构建的。这是理解 DX11 内存管理与接口调用的前提。
1 接口与生命周期
- 二进制标准:COM 是一种二进制接口标准,允许不同语言(C++, C#等)通过统一的虚函数表(vtable)调用 DirectX 驱动程序功能。
- IUnknown 接口:所有 DirectX 资源(如
ID3D11Device,ID3D11Buffer)均继承自IUnknown接口。 - 引用计数 (Reference Counting):
- DirectX 对象不由 C++ 的
new/delete关键字管理。 - 生命周期控制:当通过 API(如
D3D11CreateDevice)创建对象时,内部引用计数为 1。每当该对象被赋值或绑定到新的管线阶段时,引用计数可能增加 (AddRef)。 - 内存释放:程序员必须在不再使用对象时调用
Release()方法。当引用计数归零时,对象自动销毁并释放显存。 - 最佳实践:使用
Microsoft::WRL::ComPtr<T>智能指针来自动管理Release()调用,防止内存泄漏。
二. 硬件抽象层:Device 与 DeviceContext
DirectX 11 引入了多线程渲染支持,为此将传统的“渲染设备”概念解耦为两个核心接口。
1 ID3D11Device (虚拟适配器)
该接口代表显卡硬件(Video Adapter)的虚拟化实例。
-
核心职责:资源分配 (Resource Allocation)。
-
创建资源(纹理、缓冲区)。
-
创建视图(Views)。
-
创建着色器对象(Shaders)。
-
检查硬件功能等级(Feature Levels)。
-
线程特性:线程安全 (Thread-Safe)。允许多个线程同时调用
Create系列函数创建资源,内部实现了无锁或细粒度锁机制。
2 ID3D11DeviceContext (设备上下文)
该接口代表渲染管线的当前状态与命令列表。
-
核心职责:渲染命令发送 (Command Dispatch)。
-
绑定管线状态(Input Layout, Shaders, States)。
-
绑定资源到槽位(Vertex Buffers, Constant Buffers)。
-
执行绘制命令(Draw, Dispatch)。
-
类型:
- Immediate Context (立即上下文):直接向 GPU 驱动程序提交命令缓冲区。每个应用程序通常只有一个,非线程安全,必须在主渲染线程使用。
- Deferred Context (延迟上下文):将命令录制到命令列表(Command List),随后由立即上下文执行。用于多线程渲染优化。
三. 图形基础设施:DXGI 与 交换链
DirectX Graphics Infrastructure (DXGI) 是一个独立于 Direct3D 的子系统,负责处理与底层硬件和窗口系统的交互。
1. 交换链 (Swap Chain)
为了避免屏幕刷新时的“撕裂 (Tearing)”现象,图形渲染采用双缓冲 (Double Buffering) 或多缓冲机制。
- Front Buffer (前台缓冲区):包含当前显示器正在扫描显示的图像数据。对 CPU/GPU 通常是只读的。
- Back Buffer (后台缓冲区):GPU 正在进行光栅化和像素填充的目标内存。
- Present (呈现):
- 当一帧绘制完成,调用
IDXGISwapChain::Present。 - Flip Model:DXGI 并不拷贝像素数据,而是直接交换 Front Buffer 和 Back Buffer 的显存指针(Pointer Swap)。此操作在垂直同步(VSync)信号到来时执行,确保画面完整性。
四. 坐标系统与规范化设备坐标 (NDC)
在光栅化发生之前,所有的几何变换最终必须输出到特定的坐标系中,以便硬件进行裁剪和映射。
1 规范化设备坐标 (NDC)
无论场景中的单位是米还是公里,经过投影变换后,可视空间被压缩到一个标准立方体中:
-
坐标范围:
-
-
-
(DirectX 标准,与 OpenGL 的 [-1, 1] 不同)
-
裁剪 (Clipping):任何落在此立方体之外的顶点或图元部分,都会被硬件自动丢弃。
-
左手坐标系 (Left-Handed):DirectX 默认使用左手系(Z 轴指向屏幕内)。
2 拓扑结构 (Primitive Topology)
定义顶点流如何被解释为几何图形。
- D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST:每三个顶点构成一个独立的三角形。
- Winding Order (绕序):
- 顺时针 (Clockwise):通常定义为三角形的正面。
- 逆时针 (Counter-Clockwise):定义为背面。
- 背面剔除 (Backface Culling):光栅化阶段会自动丢弃背对摄像机的三角形,以节省像素着色器开销。
五. 最小化渲染管线数据流
绘制一个最简单的图形(如三角形),数据在 CPU 与 GPU 之间的流转过程如下:
1 阶段一:输入装配 (Input Assembler, IA)
-
输入:
-
Vertex Buffer:驻留在显存(Video Memory)中的结构化数据。
-
Input Layout:C++ 结构体与 HLSL 输入签名之间的映射描述(如
DXGI_FORMAT_R32G32B32_FLOAT对应float3)。 -
操作:GPU 根据索引缓冲区(如果有)读取顶点,根据拓扑结构组装图元。
2 阶段二:顶点着色器 (Vertex Shader, VS)
-
输入:单个顶点的属性(Model Space)。
-
核心逻辑:
-
对于 2D/UI 绘制:直接透传坐标(Pass-through),坐标需预先定义在 NDC 空间。
-
对于 3D 绘制:执行 MVP 矩阵变换 ()。
-
输出:齐次裁剪空间坐标
float4(x, y, z, w)。
3 阶段三:光栅化 (Rasterizer Stage, RS)
- 硬件固定功能(不可编程)。
- 视口变换 (Viewport Transform):将 NDC 坐标 映射到屏幕像素坐标(如 )。
- 插值 (Interpolation):计算三角形内部像素的属性(颜色、UV、法线)。使用的是重心坐标插值 (Barycentric Interpolation)。
4 阶段四:像素着色器 (Pixel Shader, PS)
- 输入:插值后的像素属性。
- 核心逻辑:决定像素的最终颜色。可以是简单的常量颜色,也可以是纹理采样结果。
- 输出:
float4颜色向量 (RGBA)。
5 阶段五:输出合并 (Output Merger, OM)
-
操作:
-
深度/模板测试 (Depth/Stencil Test):对比当前像素 Z 值与 Depth Buffer。
-
混合 (Blending):将新计算的像素颜色与 Render Target 上已有的颜色进行混合(用于半透明)。
-
结果:写入 Back Buffer。
六. 核心 API 签名参考
1 初始化阶段
// 创建设备与交换链D3D11CreateDeviceAndSwapChain( nullptr, // Adapter D3D_DRIVER_TYPE_HARDWARE, // Driver Type ..., &device, // [OUT] ID3D11Device &context // [OUT] ID3D11DeviceContext);2 资源创建
// 描述缓冲区属性D3D11_BUFFER_DESC bd = {};bd.Usage = D3D11_USAGE_DEFAULT; // GPU 读写,CPU 不可直接访问bd.ByteWidth = sizeof(Vertex) * 3; // 总字节数bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; // 绑定为顶点缓冲
// 提供初始化数据D3D11_SUBRESOURCE_DATA initData = {};initData.pSysMem = vertices; // 系统内存指针
// 创建device->CreateBuffer(&bd, &initData, &vertexBuffer);3 绘制命令
// 设置管线状态context->IASetInputLayout(layout);context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
// 设置着色器context->VSSetShader(vertexShader, nullptr, 0);context->PSSetShader(pixelShader, nullptr, 0);
// 执行绘制// 3: 顶点数量, 0: 起始偏移context->Draw(3, 0);