feat(d3d12): 完善描述符堆延迟释放机制与FreeList栈式索引管理
- 添加完整的中文Doxygen注释文档 - 实现process_deferred_release()延迟释放处理 - 添加deferred_release模板函数和current_frame_index() - 实现延迟释放队列和帧索引管理 - 详细说明FreeList栈式索引分配/释放算法 - 更新D3D12学习Wiki,添加延迟释放机制章节
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "D3D12Core.h"
|
||||
#include "D3D12CommonHeader.h"
|
||||
#include "D3D12Resources.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
namespace XEngine::graphics::d3d12::core {
|
||||
@@ -216,6 +217,7 @@ private:
|
||||
void release()
|
||||
{
|
||||
core::release(cmd_allocator);
|
||||
fence_value = 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -247,6 +249,47 @@ IDXGIFactory7* dxgi_factory{ nullptr };
|
||||
*/
|
||||
d3d12_command gfx_command;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief RTV 描述符堆
|
||||
* @details 用于存储渲染目标视图的描述符,用于渲染管线中的输出
|
||||
*/
|
||||
descriptor_heap rtv_descriptor_heap{D3D12_DESCRIPTOR_HEAP_TYPE_RTV};
|
||||
/**
|
||||
* @brief DSV 描述符堆
|
||||
* @details 用于存储深度模板视图的描述符,用于渲染管线中的深度模板测试
|
||||
*/
|
||||
descriptor_heap dsv_descriptor_heap{D3D12_DESCRIPTOR_HEAP_TYPE_DSV};
|
||||
/**
|
||||
* @brief SRV 描述符堆
|
||||
* @details 用于存储着色器资源视图的描述符,用于渲染管线中的着色器资源访问
|
||||
*/
|
||||
descriptor_heap srv_descriptor_heap{D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV};
|
||||
/**
|
||||
* @brief UAV 描述符堆
|
||||
* @details 用于存储无序访问视图的描述符,用于渲染管线中的无序访问
|
||||
*/
|
||||
descriptor_heap uav_descriptor_heap{D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV};
|
||||
|
||||
/**
|
||||
* @brief 延迟释放队列
|
||||
* @details 每个帧缓冲区对应一个待释放资源的队列,用于在渲染完成后释放资源
|
||||
*/
|
||||
utl::vector<IUnknown*> deferred_releases[frame_buffer_count]{};
|
||||
|
||||
/**
|
||||
* @brief 延迟释放标志数组
|
||||
* @details 每个帧缓冲区对应一个标志位用于记录是否需要延迟释放资源
|
||||
*/
|
||||
u32 deferred_release_flag[frame_buffer_count]{};
|
||||
|
||||
/**
|
||||
* @brief 延迟释放互斥锁
|
||||
* @details 用于保护延迟释放标志数组的并发访问,确保线程安全
|
||||
*/
|
||||
std::mutex deferred_release_mutex{};
|
||||
|
||||
// 最小支持的 Direct3D 特本级别
|
||||
constexpr D3D_FEATURE_LEVEL minumum_feature_level{ D3D_FEATURE_LEVEL_11_0 };
|
||||
|
||||
@@ -311,8 +354,48 @@ get_max_feature_level(IDXGIAdapter4* adapter)
|
||||
return feature_level_info.MaxSupportedFeatureLevel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 处理延迟释放资源
|
||||
* @details 遍历指定帧索引的延迟释放资源索引数组,释放每个资源
|
||||
* @param frame_index 要处理的帧索引
|
||||
* @note 使用 __declspec(noinline) 防止编译器内联此函数,确保在调试时能够准确断点
|
||||
*/
|
||||
void __declspec(noinline)
|
||||
process_deferred_release(u32 frame_index)
|
||||
{
|
||||
std::lock_guard lock{ deferred_release_mutex };
|
||||
|
||||
// 我们在开始的的时候清楚这个帧的标志位,如果我们在结尾的时候清除,
|
||||
// 他可能被其他线程重写.
|
||||
deferred_release_flag[frame_index] = 0;
|
||||
|
||||
rtv_descriptor_heap.process_deferred_release(frame_index);
|
||||
dsv_descriptor_heap.process_deferred_release(frame_index);
|
||||
srv_descriptor_heap.process_deferred_release(frame_index);
|
||||
uav_descriptor_heap.process_deferred_release(frame_index);
|
||||
|
||||
utl::vector<IUnknown*>& resources{ deferred_releases[frame_index] };
|
||||
if(!resources.empty())
|
||||
{
|
||||
for(auto& resource : resources) release(resource);
|
||||
resources.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
namespace detail{
|
||||
void
|
||||
deferred_release(IUnknown* resource)
|
||||
{
|
||||
const u32 frmae_idx {current_frame_index()};
|
||||
std::lock_guard lock{ deferred_release_mutex };
|
||||
deferred_releases[frmae_idx].push_back(resource);
|
||||
set_deferred_release_flag();
|
||||
}
|
||||
} // detail namespace
|
||||
|
||||
|
||||
bool
|
||||
initialize()
|
||||
@@ -326,8 +409,15 @@ initialize()
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
ComPtr<ID3D12Debug3> debug_interface;
|
||||
DXCall(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_interface)));
|
||||
debug_interface->EnableDebugLayer();
|
||||
if(SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug_interface))))
|
||||
{
|
||||
debug_interface->EnableDebugLayer();
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugStringA("warning: d3d12 debug interface is not available\n");
|
||||
}
|
||||
|
||||
dxgi_factory_flag |= DXGI_CREATE_FACTORY_DEBUG;
|
||||
}
|
||||
#endif
|
||||
@@ -351,18 +441,6 @@ initialize()
|
||||
DXCall(hr = D3D12CreateDevice(main_adapter.Get(), max_feature_level, IID_PPV_ARGS(&main_device)));
|
||||
if (FAILED(hr)) return failed_init();
|
||||
|
||||
// 为 Direct3D 12 设备设置名称
|
||||
NAME_D3D12_OBJECT(main_device, L"Main Device");
|
||||
|
||||
// 使用 placement new 在已分配的内存上构造对象
|
||||
// new (&gfx_command) 表示在 gfx_command 的地址处调用构造函数
|
||||
// 这种用法允许我们在不分配新内存的情况下,在指定内存位置构造对象
|
||||
// 常用于需要在特定内存地址构造对象,或重新初始化已存在的对象
|
||||
// 这里 gfx_command 是一个类成员变量,我们直接在其内存位置上构造 d3d12_command 对象
|
||||
// 避免了额外的内存分配,同时可以传递构造参数(main_device 和命令队列类型)
|
||||
new (&gfx_command) d3d12_command(main_device, D3D12_COMMAND_LIST_TYPE_DIRECT);
|
||||
if(!gfx_command.command_queue()) return failed_init();
|
||||
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
ComPtr<ID3D12InfoQueue> info_queue;
|
||||
@@ -374,6 +452,31 @@ initialize()
|
||||
}
|
||||
#endif // _DEBUG
|
||||
|
||||
|
||||
// 使用 placement new 在已分配的内存上构造对象
|
||||
// new (&gfx_command) 表示在 gfx_command 的地址处调用构造函数
|
||||
// 这种用法允许我们在不分配新内存的情况下,在指定内存位置构造对象
|
||||
// 常用于需要在特定内存地址构造对象,或重新初始化已存在的对象
|
||||
// 这里 gfx_command 是一个类成员变量,我们直接在其内存位置上构造 d3d12_command 对象
|
||||
// 避免了额外的内存分配,同时可以传递构造参数(main_device 和命令队列类型)
|
||||
new (&gfx_command) d3d12_command(main_device, D3D12_COMMAND_LIST_TYPE_DIRECT);
|
||||
if(!gfx_command.command_queue()) return failed_init();
|
||||
|
||||
bool result{true};
|
||||
result &= rtv_descriptor_heap.initialize(512, false);
|
||||
result &= dsv_descriptor_heap.initialize(512, false);
|
||||
result &= srv_descriptor_heap.initialize(4096, false);
|
||||
result &= uav_descriptor_heap.initialize(512, false);
|
||||
if(!result) return failed_init();
|
||||
|
||||
// 为 Direct3D 12 设备设置名称
|
||||
NAME_D3D12_OBJECT(main_device, L"Main Device");
|
||||
NAME_D3D12_OBJECT(rtv_descriptor_heap.heap(), L"RTV Descriptor Heap");
|
||||
NAME_D3D12_OBJECT(dsv_descriptor_heap.heap(), L"DSV Descriptor Heap");
|
||||
NAME_D3D12_OBJECT(srv_descriptor_heap.heap(), L"SRV Descriptor Heap");
|
||||
NAME_D3D12_OBJECT(uav_descriptor_heap.heap(), L"UAV Descriptor Heap");
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -381,8 +484,25 @@ void
|
||||
shutdown()
|
||||
{
|
||||
gfx_command.release();
|
||||
|
||||
// 注意,我们需要在所有的依赖资源之前调用延迟释放函数.
|
||||
// 否则会导致依赖当基础前资源(贴图等)的资源(着色器等)不能被释放.
|
||||
for(u32 i{ 0 }; i < frame_buffer_count; ++i)
|
||||
{
|
||||
process_deferred_release(i);
|
||||
}
|
||||
|
||||
release(dxgi_factory);
|
||||
|
||||
rtv_descriptor_heap.release();
|
||||
dsv_descriptor_heap.release();
|
||||
srv_descriptor_heap.release();
|
||||
uav_descriptor_heap.release();
|
||||
|
||||
// 某些类型仅在 shutdown/reset/clear 时使用延迟释放机制来释放资源,
|
||||
// 为了确保这些资源被正确释放,需要在 shutdown 最后额外调用一次延迟释放函数。
|
||||
process_deferred_release(0);
|
||||
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
{
|
||||
@@ -415,6 +535,11 @@ render()
|
||||
gfx_command.begin_frame();
|
||||
ID3D12GraphicsCommandList6* cmd_list{ gfx_command.command_list() };
|
||||
|
||||
const u32 frame_index{ current_frame_index() };
|
||||
if(deferred_release_flag[frame_index])
|
||||
{
|
||||
process_deferred_release(frame_index);
|
||||
}
|
||||
// 记录命令
|
||||
//
|
||||
// 完成命令记录,立即提交命令列表到命令队列执行
|
||||
@@ -428,4 +553,19 @@ device()
|
||||
return main_device;
|
||||
}
|
||||
|
||||
u32
|
||||
current_frame_index()
|
||||
{
|
||||
return gfx_command.frame_index();
|
||||
}
|
||||
|
||||
|
||||
// X86结构上的整数访问权架构原子的,所以不需要加锁
|
||||
void
|
||||
set_deferred_release_flag()
|
||||
{
|
||||
deferred_release_flag[current_frame_index()] = 1;
|
||||
}
|
||||
|
||||
|
||||
}// namespace XEngine::graphics::d3d12::core
|
||||
|
||||
Reference in New Issue
Block a user