核心变更: - 新增 d3d12_texture 和 d3d12_render_texture 类 - 新增 d3d12_texture_init_info 结构,支持三种资源创建方式 - 新增 D3D12Helpers.h,提供堆属性辅助结构 - 改用 utl::free_list 管理 surface,解决重复释放问题 - 为 d3d12_surface 添加移动语义,支持撕裂检测 文档完善: - 为 FreeList.h 和 Vector.h 添加完整 Doxygen 中文注释 - 更新 D3D12 学习 Wiki,添加 SRV、资源创建方式、纹理资源类章节 - 新增变更记录文档
304 lines
8.6 KiB
C++
304 lines
8.6 KiB
C++
#pragma once
|
||
#include "D3D12CommonHeader.h"
|
||
#include "D3D12Resources.h"
|
||
|
||
namespace XEngine::graphics::d3d12 {
|
||
|
||
/**
|
||
* @class d3d12_surface
|
||
* @brief 管理 D3D12 交换链和渲染目标的表面类
|
||
*
|
||
* @details
|
||
* 该类封装了 Direct3D 12 的交换链(Swap Chain)和后台缓冲区管理,
|
||
* 提供渲染表面的完整生命周期管理。
|
||
*
|
||
* ## 核心职责
|
||
*
|
||
* - **交换链管理**:创建和管理 IDXGISwapChain4,处理前后缓冲区交换
|
||
* - **渲染目标管理**:为每个后台缓冲区创建和维护渲染目标视图(RTV)
|
||
* - **视口和裁剪矩形**:管理渲染区域的视口和裁剪设置
|
||
* - **窗口关联**:与平台窗口绑定,处理窗口大小调整
|
||
*
|
||
* ## 多缓冲机制
|
||
*
|
||
* 使用 `frame_buffer_count`(通常为 3)个后台缓冲区实现三重缓冲:
|
||
* - 减少画面撕裂
|
||
* - 提高 CPU-GPU 并行性
|
||
* - 平滑帧率波动
|
||
*
|
||
* ## 生命周期
|
||
*
|
||
* @code
|
||
* d3d12_surface surface(window);
|
||
* surface.create_swap_chain(factory, cmd_queue, DXGI_FORMAT_R8G8B8A8_UNORM);
|
||
*
|
||
* // 渲染循环
|
||
* surface.present(); // 呈现当前帧
|
||
* surface.resize(); // 窗口大小改变时调用
|
||
*
|
||
* // 自动析构时释放资源
|
||
* @endcode
|
||
*
|
||
* @see IDXGISwapChain4
|
||
* @see ID3D12Resource
|
||
* @see descriptor_handle
|
||
*/
|
||
class d3d12_surface
|
||
{
|
||
public:
|
||
/**
|
||
* @brief 构造函数,绑定到指定窗口
|
||
*
|
||
* @param window 平台窗口对象
|
||
*
|
||
* @details
|
||
* 仅保存窗口引用,不创建交换链。
|
||
* 必须随后调用 create_swap_chain() 完成初始化。
|
||
*
|
||
* @pre window.handle() 必须返回有效的窗口句柄
|
||
*/
|
||
explicit d3d12_surface(platform::window window)
|
||
:_window(window)
|
||
{
|
||
assert(window.handle());
|
||
}
|
||
|
||
/**
|
||
* @brief 析构函数,自动释放所有资源
|
||
*
|
||
* @details
|
||
* 调用 release() 清理交换链和渲染目标资源。
|
||
*/
|
||
~d3d12_surface(){ release(); }
|
||
|
||
|
||
constexpr static u32 buffer_count{ 3 };
|
||
|
||
#if USE_STL_VECTOR
|
||
DISABLE_COPY(d3d12_surface)
|
||
|
||
/**
|
||
* @brief 移动构造函数
|
||
*
|
||
* @param o 要移动的源对象
|
||
*
|
||
* @details
|
||
* 转移所有资源所有权,将源对象置为空状态。
|
||
* 用于 vector 扩容时安全转移资源。
|
||
*/
|
||
constexpr d3d12_surface(d3d12_surface&& o) noexcept
|
||
: _swap_chain{o._swap_chain}, _window{o._window}, _current_bb_index{o._current_bb_index}
|
||
, _viewport{o._viewport}, _scissor_rect{o._scissor_rect}
|
||
, _allow_tearing{o._allow_tearing}, _present_flags{o._present_flags}
|
||
{
|
||
for (u32 i = 0; i < frame_buffer_count; ++i)
|
||
{
|
||
_render_target_data[i].resource = o._render_target_data[i].resource;
|
||
_render_target_data[i].rtv = o._render_target_data[i].rtv;
|
||
}
|
||
|
||
o.reset();
|
||
}
|
||
|
||
/**
|
||
* @brief 移动赋值运算符
|
||
*
|
||
* @param o 要移动的源对象
|
||
* @return 当前对象引用
|
||
*
|
||
* @details
|
||
* 先释放当前资源,再转移所有权。
|
||
*/
|
||
constexpr d3d12_surface& operator=(d3d12_surface&& o) noexcept
|
||
{
|
||
assert(this != &o);
|
||
if (this != &o)
|
||
{
|
||
release();
|
||
move(o);
|
||
}
|
||
return *this;
|
||
}
|
||
|
||
#else
|
||
DISABLE_COPY_AND_MOVE(d3d12_surface);
|
||
#endif
|
||
|
||
|
||
/**
|
||
* @brief 创建交换链和渲染目标视图
|
||
*
|
||
* @param factory DXGI 工厂对象,用于创建交换链
|
||
* @param cmd_queue 命令队列,交换链将与此队列同步
|
||
* @param format 后台缓冲区的像素格式(如 DXGI_FORMAT_R8G8B8A8_UNORM)
|
||
*
|
||
* @details
|
||
* 执行以下操作:
|
||
* 1. 创建 IDXGISwapChain4 对象
|
||
* 2. 获取所有后台缓冲区资源
|
||
* 3. 为每个后台缓冲区创建 RTV
|
||
* 4. 初始化视口和裁剪矩形
|
||
*
|
||
* @pre factory 和 cmd_queue 必须有效
|
||
* @pre 尚未创建交换链(或已调用 release())
|
||
*/
|
||
void create_swap_chain(IDXGIFactory7* factory, ID3D12CommandQueue* cmd_queue, DXGI_FORMAT format);
|
||
|
||
/**
|
||
* @brief 呈现当前渲染帧
|
||
*
|
||
* @details
|
||
* 调用 IDXGISwapChain::Present() 将当前后台缓冲区内容显示到屏幕。
|
||
* 呈现后,后台缓冲区索引自动递增(轮转)。
|
||
*
|
||
* @note 使用 DXGI_SWAP_EFFECT_FLIP_DISCARD 模式时,
|
||
* 呈现后当前后台缓冲区内容不再有效
|
||
*/
|
||
void present() const;
|
||
|
||
/**
|
||
* @brief 调整交换链大小以匹配窗口
|
||
*
|
||
* @details
|
||
* 当窗口大小改变时调用此方法:
|
||
* 1. 释放旧的渲染目标资源
|
||
* 2. 调整交换链缓冲区大小
|
||
* 3. 重新创建渲染目标视图
|
||
* 4. 更新视口和裁剪矩形
|
||
*
|
||
* @note 必须在 GPU 完成使用当前资源后调用
|
||
*/
|
||
void resize();
|
||
|
||
/**
|
||
* @brief 获取渲染表面宽度
|
||
* @return 宽度(像素)
|
||
*/
|
||
u32 width();
|
||
|
||
/**
|
||
* @brief 获取渲染表面高度
|
||
* @return 高度(像素)
|
||
*/
|
||
u32 height();
|
||
|
||
/**
|
||
* @brief 获取当前后台缓冲区资源
|
||
* @return 指向 ID3D12Resource 的指针
|
||
*
|
||
* @details
|
||
* 返回当前帧应渲染到的后台缓冲区。
|
||
* 索引由 _current_bb_index 指定,每次 present() 后更新。
|
||
*/
|
||
constexpr ID3D12Resource *const back_buffer() const {return _render_target_data[_current_bb_index].resource;}
|
||
|
||
/**
|
||
* @brief 获取当前后台缓冲区的渲染目标视图句柄
|
||
* @return RTV 描述符句柄
|
||
*
|
||
* @details
|
||
* 用于 OMSetRenderTargets() 绑定渲染目标。
|
||
*/
|
||
constexpr descriptor_handle rtv() const {return _render_target_data[_current_bb_index].rtv;}
|
||
|
||
/**
|
||
* @brief 获取视口描述
|
||
* @return D3D12_VIEWPORT 常量引用
|
||
*
|
||
* @details
|
||
* 用于 RSSetViewports() 设置光栅化视口。
|
||
*/
|
||
constexpr const D3D12_VIEWPORT& viewport() const {return _viewport;}
|
||
|
||
/**
|
||
* @brief 获取裁剪矩形描述
|
||
* @return D3D12_RECT 常量引用
|
||
*
|
||
* @details
|
||
* 用于 RSSetScissorRects() 设置裁剪区域。
|
||
* 裁剪区域外的像素将被丢弃。
|
||
*/
|
||
constexpr const D3D12_RECT& scissor_rect() const {return _scissor_rect;}
|
||
|
||
private:
|
||
|
||
#if USE_STL_VECTOR
|
||
constexpr void move(d3d12_surface& o)
|
||
{
|
||
_swap_chain = o._swap_chain;
|
||
for (u32 i = 0; i < frame_buffer_count; ++i)
|
||
{
|
||
_render_target_data[i] = o._render_target_data[i];
|
||
}
|
||
_window = o._window;
|
||
_current_bb_index = o._current_bb_index;
|
||
_viewport = o._viewport;
|
||
_scissor_rect = o._scissor_rect;
|
||
_allow_tearing = o._allow_tearing;
|
||
_present_flags = o._present_flags;
|
||
|
||
o.reset();
|
||
}
|
||
|
||
|
||
constexpr void reset()
|
||
{
|
||
_swap_chain = nullptr;
|
||
for (u32 i = 0; i < frame_buffer_count; ++i)
|
||
{
|
||
_render_target_data[i] = {};
|
||
}
|
||
_window = {};
|
||
_current_bb_index = 0;
|
||
_viewport = {};
|
||
_scissor_rect = {};
|
||
_allow_tearing = 0;
|
||
_present_flags = 0;
|
||
}
|
||
|
||
#endif
|
||
|
||
/**
|
||
* @brief 释放所有资源
|
||
*
|
||
* @details
|
||
* 释放交换链和所有后台缓冲区资源。
|
||
* 使用延迟释放机制确保 GPU 安全。
|
||
*/
|
||
void release();
|
||
|
||
/**
|
||
* @brief 完成资源创建的最终设置
|
||
*
|
||
* @details
|
||
* 在创建或调整大小后调用,设置视口和裁剪矩形。
|
||
*/
|
||
void finalize();
|
||
|
||
/**
|
||
* @struct render_target_data
|
||
* @brief 单个后台缓冲区的渲染目标数据
|
||
*
|
||
* @details
|
||
* 每个后台缓冲区对应一个此结构体,存储:
|
||
* - 资源指针:实际的缓冲区纹理资源
|
||
* - RTV 句柄:用于绑定到渲染管线
|
||
*/
|
||
struct render_target_data
|
||
{
|
||
ID3D12Resource* resource{nullptr}; ///< 后台缓冲区资源
|
||
descriptor_handle rtv{}; ///< 渲染目标视图描述符句柄
|
||
};
|
||
|
||
IDXGISwapChain4* _swap_chain{nullptr}; ///< DXGI 交换链对象
|
||
render_target_data _render_target_data[buffer_count]; ///< 后台缓冲区数据数组
|
||
platform::window _window{}; ///< 关联的平台窗口
|
||
mutable u32 _current_bb_index{0}; ///< 当前后台缓冲区索引
|
||
D3D12_VIEWPORT _viewport{}; ///< 视口描述
|
||
D3D12_RECT _scissor_rect{}; ///< 裁剪矩形
|
||
u32 _allow_tearing{ 0 }; ///< 是否允许撕裂
|
||
u32 _present_flags{ 0 }; ///< 呈现标志位
|
||
};
|
||
|
||
}
|