# 变更记录:命令队列与多帧缓冲 **提交日期**: 2026-03-26 **提交哈希**: `26e18bd` **变更类型**: 功能实现 --- ## 变更概述 本次提交实现了 D3D12 命令队列管理类 `d3d12_command`,支持多帧缓冲渲染架构,实现了 CPU-GPU 并行渲染的基础设施。 ## 修改文件 ### Engine/Graphics/Direct3D12/ | 文件 | 变更说明 | |------|----------| | `D3D12Core.cpp` | 添加 `d3d12_command` 类,实现命令队列和命令列表管理 | | `D3D12Core.h` | 添加 `render()` 函数声明 | | `D3D12CommonHeader.h` | 添加 `frame_buffer_count` 常量和 `NAME_D3D12_OBJECT_INDEXED` 宏 | ### Engine/Graphics/ | 文件 | 变更说明 | |------|----------| | `GraphicsPlatformInterface.h` | 添加 `render` 函数指针 | | `Renderer.h` | 添加 `render()` 函数声明 | | `Renderer.cpp` | 实现 `render()` 函数 | --- ## 技术要点 ### 1. 多帧缓冲架构 ```cpp constexpr u32 frame_buffer_count{ 3 }; ``` 采用三重缓冲设计,允许 CPU 提前录制命令,GPU 异步执行,最大化硬件利用率。 ### 2. d3d12_command 类 ```cpp class d3d12_command { // 创建命令队列(支持 Direct/Compute/Copy 三种类型) // 为每帧创建独立的命令分配器 // 创建命令列表 void begin_frame(); // 等待帧完成,重置分配器和命令列表 void end_frame(); // 关闭命令列表,提交执行 }; ``` ### 3. 帧索引轮转机制 ```cpp _frame_index = (_frame_index + 1) % frame_buffer_count; ``` 环形缓冲区管理帧资源,确保 CPU 不会超前 GPU 超过 3 帧。 ### 4. command_frame 结构 ```cpp struct command_frame { ID3D12CommandAllocator* cmd_allocator{ nullptr }; void wait(); // 等待 GPU 完成该帧 void release(); // 释放资源 }; ``` 每帧独立的命令分配器,避免 GPU 执行期间重置冲突。 ### 5. 命名调试宏 ```cpp #define NAME_D3D12_OBJECT_INDEXED(obj, n, name) \ obj->SetName(full_name); ... ``` 支持为多个同类对象设置带索引的调试名称。 --- ## 渲染流程 ``` render() │ ├─► begin_frame() │ ├─► 等待当前帧 GPU 完成 │ ├─► 重置命令分配器 │ └─► 重置命令列表 │ └─► end_frame() ├─► 关闭命令列表 ├─► 提交命令列表执行 └─► 递增帧索引 ``` --- ## 设计原理 ### 为什么需要多帧缓冲? 1. **CPU-GPU 并行性**: 单缓冲模式下 CPU 必须等待 GPU 完成,多帧缓冲允许 CPU 提前录制 N 帧 2. **命令分配器冲突解决**: D3D12 中命令分配器在 GPU 执行期间不能被重置,每帧独立分配器解决此问题 3. **帧时序稳定性**: 缓冲 N 帧可平滑帧率波动 ### 命令列表创建后立即关闭 ```cpp DXCall(_cmd_list->Close()); ``` 新创建的命令列表处于"录制打开"状态,立即关闭使其进入可提交状态,这是一种防御性编程。 --- ## 后续工作 - [ ] 实现 Fence 同步机制 - [ ] 实现交换链 - [ ] 实现描述符堆 --- ## 相关文档 - [D3D12学习Wiki](../wiki/D3D12学习Wiki.md)