Files
DX12/Engine/Graphics/Direct3D12/D3D12Surface.h
SpecialX 4d13d8df89 feat(d3d12): 新增纹理资源类,修复 Surface 重复释放问题
核心变更:
- 新增 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、资源创建方式、纹理资源类章节
- 新增变更记录文档
2026-04-01 16:17:42 +08:00

304 lines
8.6 KiB
C++
Raw 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.
#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 }; ///< 呈现标志位
};
}