feat: implement Fence synchronization for CPU-GPU frame sync
D3D12 Core: - Add ID3D12Fence1 and fence_value to d3d12_command - Add fence_event for CPU waiting - Implement wait() in command_frame for frame sync - Implement flush() to wait all frames complete - Add fence_value tracking per frame - Signal fence at end_frame with incremented value TestRenderer: - Call graphics::render() in run() Documentation: - Add changelog for Fence sync implementation - Update D3D12 Wiki with Fence sync section
This commit is contained in:
154
docs/changelogs/2026-03/20260327-d3d12-fence-sync.md
Normal file
154
docs/changelogs/2026-03/20260327-d3d12-fence-sync.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# 变更记录:Fence 同步机制实现
|
||||
|
||||
**提交日期**: 2026-03-27
|
||||
**提交哈希**: `b00a906`
|
||||
**变更类型**: 功能实现
|
||||
|
||||
---
|
||||
|
||||
## 变更概述
|
||||
|
||||
本次提交实现了 D3D12 Fence(围栏)同步机制,完成 CPU-GPU 帧同步,确保命令列表执行顺序正确,避免资源冲突。
|
||||
|
||||
## 修改文件
|
||||
|
||||
### Engine/Graphics/Direct3D12/
|
||||
|
||||
| 文件 | 变更说明 |
|
||||
|------|----------|
|
||||
| `D3D12Core.cpp` | 添加 Fence 同步机制,实现 `wait()`、`flush()` 方法 |
|
||||
|
||||
### EngineTest/
|
||||
|
||||
| 文件 | 变更说明 |
|
||||
|------|----------|
|
||||
| `TestRenderer.cpp` | 在 `run()` 中调用 `graphics::render()` |
|
||||
|
||||
---
|
||||
|
||||
## 技术要点
|
||||
|
||||
### 1. Fence 对象
|
||||
|
||||
```cpp
|
||||
ID3D12Fence1* _fence{ nullptr };
|
||||
u64 _fence_value{ 0 };
|
||||
HANDLE _fence_event{ nullptr };
|
||||
```
|
||||
|
||||
- **Fence**: GPU 可设置的计数器,用于同步
|
||||
- **Fence Value**: 64位无符号整数,每帧递增
|
||||
- **Fence Event**: Windows 事件对象,用于 CPU 等待
|
||||
|
||||
### 2. command_frame 结构
|
||||
|
||||
```cpp
|
||||
struct command_frame
|
||||
{
|
||||
ID3D12CommandAllocator* cmd_allocator{ nullptr };
|
||||
u64 fence_value{ 0 }; // 该帧的围栏值
|
||||
|
||||
void wait(HANDLE fence_event, ID3D12Fence1* fence);
|
||||
};
|
||||
```
|
||||
|
||||
每帧记录其围栏值,用于判断 GPU 是否完成该帧。
|
||||
|
||||
### 3. 帧同步等待
|
||||
|
||||
```cpp
|
||||
void wait(HANDLE fence_event, ID3D12Fence1* fence)
|
||||
{
|
||||
if(fence->GetCompletedValue() < fence_value)
|
||||
{
|
||||
fence->SetEventOnCompletion(fence_value, fence_event);
|
||||
WaitForSingleObject(fence_event, INFINITE);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 检查 GPU 是否完成到目标围栏值
|
||||
- 未完成则设置事件并等待
|
||||
|
||||
### 4. 帧结束信号
|
||||
|
||||
```cpp
|
||||
void end_frame()
|
||||
{
|
||||
// ... 提交命令列表 ...
|
||||
++_fence_value;
|
||||
_cmd_frames[_frame_index].fence_value = _fence_value;
|
||||
_cmd_queue->Signal(_fence, _fence_value);
|
||||
_frame_index = (_frame_index + 1) % frame_buffer_count;
|
||||
}
|
||||
```
|
||||
|
||||
- 递增围栏值
|
||||
- 记录当前帧的围栏值
|
||||
- 向 GPU 发送信号
|
||||
|
||||
### 5. flush 方法
|
||||
|
||||
```cpp
|
||||
void flush()
|
||||
{
|
||||
for(u32 i{ 0 }; i < frame_buffer_count; ++i)
|
||||
{
|
||||
_cmd_frames[i].wait(_fence_event, _fence);
|
||||
}
|
||||
_frame_index = 0;
|
||||
}
|
||||
```
|
||||
|
||||
等待所有帧完成,用于资源释放前确保 GPU 完成。
|
||||
|
||||
---
|
||||
|
||||
## 同步流程
|
||||
|
||||
```
|
||||
begin_frame()
|
||||
│
|
||||
├─► 检查当前帧的 fence_value
|
||||
│
|
||||
└─► 如果 GPU 未完成,CPU 等待
|
||||
│
|
||||
└─► 重置分配器和命令列表
|
||||
|
||||
end_frame()
|
||||
│
|
||||
├─► 提交命令列表
|
||||
│
|
||||
├─► ++fence_value
|
||||
│
|
||||
├─► 记录当前帧的 fence_value
|
||||
│
|
||||
├─► Signal(fence, fence_value)
|
||||
│
|
||||
└─► 递增帧索引
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 围栏值溢出问题
|
||||
|
||||
```cpp
|
||||
// 64位无符号整数,即便每秒1000帧,也需要5.8亿年才能回绕
|
||||
u64 _fence_value{ 0 };
|
||||
```
|
||||
|
||||
无需担心溢出问题。
|
||||
|
||||
---
|
||||
|
||||
## 后续工作
|
||||
|
||||
- [ ] 交换链实现
|
||||
- [ ] 描述符堆
|
||||
- [ ] 渲染目标视图
|
||||
|
||||
---
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [D3D12学习Wiki](../wiki/D3D12学习Wiki.md)
|
||||
Reference in New Issue
Block a user