Files
DX12/docs/changelogs/2026-03/20260327-d3d12-fence-sync.md
SpecialX f1584ec3c6 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
2026-03-27 18:56:43 +08:00

155 lines
2.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 变更记录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)