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

2.9 KiB
Raw Permalink Blame History

变更记录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 对象

ID3D12Fence1* _fence{ nullptr };
u64 _fence_value{ 0 };
HANDLE _fence_event{ nullptr };
  • Fence: GPU 可设置的计数器,用于同步
  • Fence Value: 64位无符号整数每帧递增
  • Fence Event: Windows 事件对象,用于 CPU 等待

2. command_frame 结构

struct command_frame
{
    ID3D12CommandAllocator* cmd_allocator{ nullptr };
    u64 fence_value{ 0 };  // 该帧的围栏值

    void wait(HANDLE fence_event, ID3D12Fence1* fence);
};

每帧记录其围栏值,用于判断 GPU 是否完成该帧。

3. 帧同步等待

void wait(HANDLE fence_event, ID3D12Fence1* fence)
{
    if(fence->GetCompletedValue() < fence_value)
    {
        fence->SetEventOnCompletion(fence_value, fence_event);
        WaitForSingleObject(fence_event, INFINITE);
    }
}
  • 检查 GPU 是否完成到目标围栏值
  • 未完成则设置事件并等待

4. 帧结束信号

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 方法

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)
    │
    └─► 递增帧索引

围栏值溢出问题

// 64位无符号整数即便每秒1000帧也需要5.8亿年才能回绕
u64 _fence_value{ 0 };

无需担心溢出问题。


后续工作

  • 交换链实现
  • 描述符堆
  • 渲染目标视图

相关文档