feat(d3d12): 实现交换链与渲染表面管理
- 新增 d3d12_surface 类,管理交换链和渲染目标 - 实现三重缓冲后台缓冲区管理 - 添加视口和裁剪矩形配置 - 修复 GraphicsPlatformInterface.h 循环包含问题 - 添加完整的中文 Doxygen 注释 - 更新 D3D12 学习 Wiki,添加交换链章节
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "CommonHeader.h"
|
||||
#include "Graphics\Renderer.h"
|
||||
#include "Platform\Window.h"
|
||||
|
||||
// 引入 DirectX Graphics Infrastructure(DXGI)6.0
|
||||
// 头文件,用于访问 DXGI 接口(如 IDXGIFactory6、
|
||||
@@ -23,7 +24,7 @@
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
#pragma comment(lib, "d3d12.lib")
|
||||
|
||||
namespace XEngine::graphics::d3d12::core {
|
||||
namespace XEngine::graphics::d3d12 {
|
||||
constexpr u32 frame_buffer_count{ 3 };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "D3D12Core.h"
|
||||
#include "D3D12Core.h"
|
||||
#include "D3D12CommonHeader.h"
|
||||
#include "D3D12Resources.h"
|
||||
|
||||
@@ -290,6 +290,9 @@ u32 deferred_release_flag[frame_buffer_count]{};
|
||||
*/
|
||||
std::mutex deferred_release_mutex{};
|
||||
|
||||
// 默认渲染目标格式
|
||||
constexpr DXGI_FORMAT render_target_format{ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB };
|
||||
|
||||
// 最小支持的 Direct3D 特本级别
|
||||
constexpr D3D_FEATURE_LEVEL minumum_feature_level{ D3D_FEATURE_LEVEL_11_0 };
|
||||
|
||||
@@ -553,12 +556,25 @@ device()
|
||||
return main_device;
|
||||
}
|
||||
|
||||
descriptor_heap& rtv_heap() {return rtv_descriptor_heap;}
|
||||
descriptor_heap& dsv_heap() {return dsv_descriptor_heap;}
|
||||
descriptor_heap& uav_heap() {return uav_descriptor_heap;}
|
||||
descriptor_heap& srv_heap() {return srv_descriptor_heap;}
|
||||
|
||||
|
||||
u32
|
||||
current_frame_index()
|
||||
{
|
||||
return gfx_command.frame_index();
|
||||
}
|
||||
|
||||
DXGI_FORMAT
|
||||
default_render_target_format()
|
||||
{
|
||||
return render_target_format;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// X86结构上的整数访问权架构原子的,所以不需要加锁
|
||||
void
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "D3D12CommonHeader.h"
|
||||
|
||||
/**
|
||||
* @brief Direct3D 12 核心类
|
||||
* @details 定义了 Direct3D 12 核心功能的初始化与关闭函数
|
||||
*/
|
||||
|
||||
namespace XEngine::graphics::d3d12 {
|
||||
class descriptor_heap;
|
||||
}
|
||||
|
||||
namespace XEngine::graphics::d3d12::core{
|
||||
/**
|
||||
* @brief 初始化 Direct3D 12 核心功能
|
||||
@@ -81,10 +86,24 @@ ID3D12Device *const device();
|
||||
*/
|
||||
u32 current_frame_index();
|
||||
|
||||
descriptor_heap& rtv_heap();
|
||||
descriptor_heap& dsv_heap();
|
||||
descriptor_heap& srv_heap();
|
||||
descriptor_heap& uav_heap();
|
||||
|
||||
/**
|
||||
* @brief 获取默认渲染目标格式
|
||||
* @details 返回 Direct3D 12 设备的默认渲染目标格式
|
||||
* @return DXGI_FORMAT 默认渲染目标格式
|
||||
*/
|
||||
DXGI_FORMAT default_render_target_format();
|
||||
|
||||
/**
|
||||
* @brief 设置延迟释放标志
|
||||
* @details 用于在渲染循环中设置延迟释放标志,通知资源管理器在当前帧渲染完成后释放资源
|
||||
*/
|
||||
void set_deferred_release_flag();
|
||||
|
||||
|
||||
|
||||
}// namespace XEngine::graphics::d3d12::core
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "CommonHeader.h"
|
||||
#include "D3D12Interface.h"
|
||||
#include "D3D12Core.h"
|
||||
#include "Graphics\GraphicsPlatformInterface.h"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "D3D12Resources.h"
|
||||
#include "D3D12Resources.h"
|
||||
#include "D3D12Core.h"
|
||||
|
||||
namespace XEngine::graphics::d3d12::core{
|
||||
namespace XEngine::graphics::d3d12{
|
||||
//////////// DESCRIPTOR HEAP ////////////
|
||||
// 该类将被多个线程并发访问:资源创建(如纹理)与资源销毁/释放可能发生在不同线程,
|
||||
// 因此需要同步机制保护内部数据结构
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "D3D12CommonHeader.h"
|
||||
|
||||
namespace XEngine::graphics::d3d12::core {
|
||||
namespace XEngine::graphics::d3d12 {
|
||||
|
||||
/**
|
||||
* @brief 前向声明,用于友元类关系
|
||||
@@ -417,4 +417,4 @@ private:
|
||||
const D3D12_DESCRIPTOR_HEAP_TYPE _type;
|
||||
};
|
||||
|
||||
} // namespace XEngine::graphics::d3d12::core
|
||||
} // namespace XEngine::graphics::d3d12
|
||||
|
||||
114
Engine/Graphics/Direct3D12/D3D12Surface.cpp
Normal file
114
Engine/Graphics/Direct3D12/D3D12Surface.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "D3D12Surface.h"
|
||||
#include "D3D12Core.h"
|
||||
|
||||
|
||||
namespace XEngine::graphics::d3d12 {
|
||||
namespace{
|
||||
constexpr DXGI_FORMAT
|
||||
to_non_srgb(DXGI_FORMAT format)
|
||||
{
|
||||
if(format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) return DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
return format;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
d3d12_surface::create_swap_chain(IDXGIFactory7* factory, ID3D12CommandQueue* cmd_queue, DXGI_FORMAT format)
|
||||
{
|
||||
assert(!factory && cmd_queue);
|
||||
|
||||
// 应为可以多次调用,所以需要先释放旧的 swap chain
|
||||
release();
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 desc{};
|
||||
desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; // Alpha通道模式(窗口透明度相关)
|
||||
desc.BufferCount = frame_buffer_count; // 后台缓冲区数量(用于双缓冲/多缓冲)
|
||||
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 缓冲区用途(作为渲染目标输出)
|
||||
desc.Flags = 0; // 交换链标志位(无)
|
||||
desc.Format = to_non_srgb(format); // 像素格式(转换为非SRGB格式)
|
||||
desc.Height = _window.height(); // 交换链高度(与窗口高度一致)
|
||||
desc.Width = _window.width(); // 交换链宽度(与窗口宽度一致)
|
||||
desc.SampleDesc.Count = 1; // 多重采样数量(1表示禁用MSAA)
|
||||
desc.SampleDesc.Quality = 0; // 多重采样质量等级(0表示禁用)
|
||||
desc.Scaling = DXGI_SCALING_STRETCH; // 缓冲区缩放模式(窗口大小变化时的处理方式)
|
||||
desc.Stereo = false; // 立体显示模式(3D显示)
|
||||
|
||||
IDXGISwapChain1* swap_chain;
|
||||
HWND hwnd {(HWND)_window.handle()};
|
||||
// 为窗口创建交换链,指定交换链描述结构体
|
||||
DXCall(factory->CreateSwapChainForHwnd(cmd_queue, hwnd, &desc, nullptr, nullptr, &swap_chain));
|
||||
// 控制 DXGI 如何响应特定的系统按键,阻止 DXGI 响应 Alt+Enter 按键序列,由应用程序手动处理全屏切换逻辑。
|
||||
DXCall(factory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER));
|
||||
// 将局部变量 swap_chain 创建的交换链对象“转移”给类成员 _swap_chain 来长期持有。
|
||||
// 免了直接赋值(_swap_chain = swap_chain)可能导致的引用计数问题:直接赋值不增加
|
||||
// 计数,后续释放 swap_chain 会使对象计数归零而被销毁,_swap_chain 就会变成悬空指针。
|
||||
DXCall(swap_chain->QueryInterface(IID_PPV_ARGS(&_swap_chain)));
|
||||
core::release(swap_chain);
|
||||
|
||||
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
|
||||
|
||||
for(u32 i = 0; i < frame_buffer_count; ++i)
|
||||
{
|
||||
_render_target_data[i].rtv = core::rtv_heap().allocate();
|
||||
}
|
||||
|
||||
finalize();
|
||||
}
|
||||
|
||||
void
|
||||
d3d12_surface::present() const
|
||||
{
|
||||
assert(_swap_chain);
|
||||
// 设置是否使用垂直同步
|
||||
// 0 表示不使用垂直同步,1 表示使用垂直同步
|
||||
// 第二个参数设置有无特殊行为,0 表示无特殊行为
|
||||
DXCall(_swap_chain->Present(0, 0));
|
||||
_current_bb_index = _swap_chain->GetCurrentBackBufferIndex();
|
||||
}
|
||||
|
||||
void
|
||||
d3d12_surface::finalize()
|
||||
{
|
||||
// 为每个缓冲区创建渲染目标视图
|
||||
for(u32 i = 0; i < frame_buffer_count; ++i)
|
||||
{
|
||||
render_target_data& data{ _render_target_data[i] };
|
||||
assert(!data.resource);
|
||||
DXCall(_swap_chain->GetBuffer(i, IID_PPV_ARGS(&data.resource)));
|
||||
D3D12_RENDER_TARGET_VIEW_DESC desc{};
|
||||
desc.Format = core::default_render_target_format();
|
||||
desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||
core::device()->CreateRenderTargetView(data.resource, &desc, data.rtv.cpu);
|
||||
}
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC desc{};
|
||||
DXCall(_swap_chain->GetDesc(&desc));
|
||||
const u32 width{ desc.BufferDesc.Width };
|
||||
const u32 height{ desc.BufferDesc.Height };
|
||||
assert(_window.width() == width && _window.height() == height);
|
||||
|
||||
|
||||
_viewport.TopLeftX = 0.0f;
|
||||
_viewport.TopLeftY = 0.0f;
|
||||
_viewport.Width = (float)width;
|
||||
_viewport.Height = (float)height;
|
||||
_viewport.MaxDepth = 1.0f;
|
||||
_viewport.MinDepth = 0.0f;
|
||||
|
||||
_scissor_rect = {0, 0, (s32)width, (s32)height};
|
||||
}
|
||||
|
||||
void
|
||||
d3d12_surface::release()
|
||||
{
|
||||
for(u32 i = 0; i < frame_buffer_count; ++i)
|
||||
{
|
||||
render_target_data& data{ _render_target_data[i] };
|
||||
core::release(data.resource);
|
||||
core::rtv_heap().free(data.rtv);
|
||||
}
|
||||
|
||||
core::release(_swap_chain);
|
||||
}
|
||||
|
||||
} // namespace XEngine::graphics::d3d12
|
||||
210
Engine/Graphics/Direct3D12/D3D12Surface.h
Normal file
210
Engine/Graphics/Direct3D12/D3D12Surface.h
Normal file
@@ -0,0 +1,210 @@
|
||||
#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();}
|
||||
|
||||
/**
|
||||
* @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:
|
||||
/**
|
||||
* @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[frame_buffer_count];///< 后台缓冲区数据数组
|
||||
platform::window _window{}; ///< 关联的平台窗口
|
||||
mutable u32 _current_bb_index{0}; ///< 当前后台缓冲区索引
|
||||
D3D12_VIEWPORT _viewport{}; ///< 视口描述
|
||||
D3D12_RECT _scissor_rect{}; ///< 裁剪矩形
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user