# 变更记录: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)