核心变更: - 新增 d3d12_texture 和 d3d12_render_texture 类 - 新增 d3d12_texture_init_info 结构,支持三种资源创建方式 - 新增 D3D12Helpers.h,提供堆属性辅助结构 - 改用 utl::free_list 管理 surface,解决重复释放问题 - 为 d3d12_surface 添加移动语义,支持撕裂检测 文档完善: - 为 FreeList.h 和 Vector.h 添加完整 Doxygen 中文注释 - 更新 D3D12 学习 Wiki,添加 SRV、资源创建方式、纹理资源类章节 - 新增变更记录文档
7.0 KiB
7.0 KiB
变更记录:交换链与渲染表面实现
提交日期: 2026-03-31
提交哈希: 95d8893
变更类型: 功能新增
变更概述
本次提交实现了 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 类设计
核心职责
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. 交换链创建流程
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. 渲染目标视图创建
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)
D3D12_VIEWPORT _viewport{
.TopLeftX = 0.0f,
.TopLeftY = 0.0f,
.Width = (float)width,
.Height = (float)height,
.MinDepth = 0.0f,
.MaxDepth = 1.0f
};
定义光栅化阶段的渲染区域和深度范围。
裁剪矩形(Scissor Rect)
D3D12_RECT _scissor_rect{0, 0, (s32)width, (s32)height};
定义像素输出的裁剪区域,区域外的像素将被丢弃。
后续工作
- 实现深度模板视图
- 渲染第一个三角形
- 实现根签名和管线状态对象