feat(d3d12): 实现交换链与渲染表面管理
- 新增 d3d12_surface 类,管理交换链和渲染目标 - 实现三重缓冲后台缓冲区管理 - 添加视口和裁剪矩形配置 - 修复 GraphicsPlatformInterface.h 循环包含问题 - 添加完整的中文 Doxygen 注释 - 更新 D3D12 学习 Wiki,添加交换链章节
This commit is contained in:
231
docs/changelogs/2026-03/20260331-d3d12-swap-chain.md
Normal file
231
docs/changelogs/2026-03/20260331-d3d12-swap-chain.md
Normal file
@@ -0,0 +1,231 @@
|
||||
# 变更记录:交换链与渲染表面实现
|
||||
|
||||
**提交日期**: 2026-03-31
|
||||
**提交哈希**: `待定`
|
||||
**变更类型**: 功能新增
|
||||
|
||||
---
|
||||
|
||||
## 变更概述
|
||||
|
||||
本次提交实现了 D3D12 交换链(Swap Chain)和渲染表面管理,完成了渲染输出的基础架构。同时修复了循环包含问题,确保编译正确。
|
||||
|
||||
## 修改文件
|
||||
|
||||
### 新增文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `D3D12Surface.h` | 交换链和渲染目标管理类头文件 |
|
||||
| `D3D12Surface.cpp` | 交换链实现 |
|
||||
|
||||
### 修改文件
|
||||
|
||||
| 文件 | 变更说明 |
|
||||
|------|----------|
|
||||
| `GraphicsPlatformInterface.h` | 修复循环包含,添加 `surface_id` 定义 |
|
||||
| `Renderer.h` | 移除循环包含,保持类型定义 |
|
||||
| `Renderer.cpp` | 实现表面相关函数 |
|
||||
| `D3D12Core.h/cpp` | 添加描述符堆访问函数 |
|
||||
| `D3D12Interface.cpp` | 更新平台接口 |
|
||||
| `D3D12CommonHeader.h` | 编码修复 |
|
||||
|
||||
---
|
||||
|
||||
## 技术要点
|
||||
|
||||
### 1. 交换链(Swap Chain)原理
|
||||
|
||||
#### 什么是交换链?
|
||||
|
||||
交换链是 DXGI 提供的机制,用于管理前后缓冲区的交换:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 交换链工作原理 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ 后台缓冲 │ │ 后台缓冲 │ │ 后台缓冲 │ │
|
||||
│ │ 0 │ │ 1 │ │ 2 │ │
|
||||
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
||||
│ │ │ │ │
|
||||
│ └─────────────┼─────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Present() │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ 前台缓冲 │ ───► 显示器 │
|
||||
│ └──────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 三重缓冲优势
|
||||
|
||||
| 特性 | 说明 |
|
||||
|------|------|
|
||||
| **减少撕裂** | 前台缓冲独立于后台缓冲,避免部分更新 |
|
||||
| **提高并行性** | CPU 可提前录制多帧命令 |
|
||||
| **平滑帧率** | 缓冲区平滑帧时间波动 |
|
||||
|
||||
### 2. d3d12_surface 类设计
|
||||
|
||||
#### 核心职责
|
||||
|
||||
```cpp
|
||||
class d3d12_surface
|
||||
{
|
||||
// 交换链管理
|
||||
IDXGISwapChain4* _swap_chain;
|
||||
|
||||
// 渲染目标管理(每个后台缓冲区一个)
|
||||
render_target_data _render_target_data[frame_buffer_count];
|
||||
|
||||
// 视口和裁剪
|
||||
D3D12_VIEWPORT _viewport;
|
||||
D3D12_RECT _scissor_rect;
|
||||
};
|
||||
```
|
||||
|
||||
#### 生命周期
|
||||
|
||||
```
|
||||
构造 d3d12_surface(window)
|
||||
│
|
||||
▼
|
||||
create_swap_chain(factory, cmd_queue, format)
|
||||
│
|
||||
├─► 创建 IDXGISwapChain4
|
||||
├─► 分配 RTV 描述符
|
||||
└─► 初始化视口/裁剪矩形
|
||||
│
|
||||
▼
|
||||
渲染循环
|
||||
│
|
||||
├─► back_buffer() 获取当前缓冲区
|
||||
├─► rtv() 获取渲染目标视图
|
||||
└─► present() 呈现帧
|
||||
│
|
||||
▼
|
||||
析构 ~d3d12_surface()
|
||||
│
|
||||
└─► release() 释放资源
|
||||
```
|
||||
|
||||
### 3. 交换链创建流程
|
||||
|
||||
```cpp
|
||||
void d3d12_surface::create_swap_chain(...)
|
||||
{
|
||||
// 1. 配置交换链描述
|
||||
DXGI_SWAP_CHAIN_DESC1 desc{};
|
||||
desc.BufferCount = frame_buffer_count; // 3 个后台缓冲区
|
||||
desc.Format = to_non_srgb(format); // 像素格式
|
||||
desc.Width = _window.width();
|
||||
desc.Height = _window.height();
|
||||
desc.SampleDesc.Count = 1; // 禁用 MSAA
|
||||
|
||||
// 2. 创建交换链
|
||||
factory->CreateSwapChainForHwnd(cmd_queue, hwnd, &desc, ...);
|
||||
|
||||
// 3. 禁用 Alt+Enter 全屏切换(由应用处理)
|
||||
factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER);
|
||||
|
||||
// 4. 分配 RTV 描述符
|
||||
for(u32 i = 0; i < frame_buffer_count; ++i)
|
||||
{
|
||||
_render_target_data[i].rtv = core::rtv_heap().allocate();
|
||||
}
|
||||
|
||||
// 5. 创建渲染目标视图
|
||||
finalize();
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 渲染目标视图创建
|
||||
|
||||
```cpp
|
||||
void d3d12_surface::finalize()
|
||||
{
|
||||
for(u32 i = 0; i < frame_buffer_count; ++i)
|
||||
{
|
||||
// 获取后台缓冲区资源
|
||||
_swap_chain->GetBuffer(i, IID_PPV_ARGS(&data.resource));
|
||||
|
||||
// 创建渲染目标视图
|
||||
D3D12_RENDER_TARGET_VIEW_DESC desc{};
|
||||
desc.Format = core::default_render_target_format();
|
||||
desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||
|
||||
core::device()->CreateRenderTargetView(data.resource, &desc, data.rtv.cpu);
|
||||
}
|
||||
|
||||
// 设置视口和裁剪矩形
|
||||
_viewport = {0, 0, (float)width, (float)height, 0.0f, 1.0f};
|
||||
_scissor_rect = {0, 0, (s32)width, (s32)height};
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 循环包含修复
|
||||
|
||||
#### 问题
|
||||
|
||||
```
|
||||
GraphicsPlatformInterface.h → Renderer.h → GraphicsPlatformInterface.h (循环!)
|
||||
```
|
||||
|
||||
#### 解决方案
|
||||
|
||||
将 `surface_id` 定义移到 `Renderer.h`,`GraphicsPlatformInterface.h` 包含 `Renderer.h`:
|
||||
|
||||
```
|
||||
Renderer.h (定义 surface_id)
|
||||
↓
|
||||
GraphicsPlatformInterface.h (使用 surface_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 视口与裁剪矩形
|
||||
|
||||
### 视口(Viewport)
|
||||
|
||||
```cpp
|
||||
D3D12_VIEWPORT _viewport{
|
||||
.TopLeftX = 0.0f,
|
||||
.TopLeftY = 0.0f,
|
||||
.Width = (float)width,
|
||||
.Height = (float)height,
|
||||
.MinDepth = 0.0f,
|
||||
.MaxDepth = 1.0f
|
||||
};
|
||||
```
|
||||
|
||||
定义光栅化阶段的渲染区域和深度范围。
|
||||
|
||||
### 裁剪矩形(Scissor Rect)
|
||||
|
||||
```cpp
|
||||
D3D12_RECT _scissor_rect{0, 0, (s32)width, (s32)height};
|
||||
```
|
||||
|
||||
定义像素输出的裁剪区域,区域外的像素将被丢弃。
|
||||
|
||||
---
|
||||
|
||||
## 后续工作
|
||||
|
||||
- [ ] 实现深度模板视图
|
||||
- [ ] 渲染第一个三角形
|
||||
- [ ] 实现根签名和管线状态对象
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [D3D12学习Wiki](../wiki/D3D12学习Wiki.md)
|
||||
Reference in New Issue
Block a user